package com.alibaba.citrus.cr.forecast.functions.forecastorder.ability;

import com.alibaba.citrus.cr.common.annotation.EpochTransactional;
import com.alibaba.citrus.cr.common.annotation.FunctionInvoker;
import com.alibaba.citrus.cr.common.annotation.ProcessInvoker;
import com.alibaba.citrus.cr.common.exception.FunctionException;
import com.alibaba.citrus.cr.common.model.ApiContext;
import com.alibaba.citrus.cr.forecast.common.DateUtils;
import com.alibaba.citrus.cr.forecast.common.LogUtils;
import com.alibaba.citrus.cr.forecast.common.MoneyUtil;
import com.alibaba.citrus.cr.forecast.functions.forecastorder.constants.ForecastConstants;
import com.alibaba.citrus.cr.forecast.functions.forecastorder.entity.ForecastOrderEntity;
import com.alibaba.citrus.cr.forecast.functions.forecastorder.entity.ForecastOrderLineEntity;
import com.alibaba.citrus.cr.forecast.functions.forecastorder.factory.ForecastOrderFactory;
import com.alibaba.citrus.cr.forecast.functions.model.AnalyzeProcessDO;
import com.alibaba.citrus.cr.forecast.repository.*;
import com.alibaba.citrus.cr.forecast.repository.impl.converter.ForecastOrderRepositoryConverter;
import com.alibaba.citrus.cr.sales.plan.api.salesplan.SalesPlanLineQueryService;
import com.alibaba.citrus.cr.sales.plan.api.salesplan.SalesPlanQueryService;
import com.alibaba.citrus.cr.sales.plan.dto.api.request.PageQuerySalePlanRequest;
import com.alibaba.citrus.cr.sales.plan.sdo.SalesPlanSDO;
import com.alibaba.cz.base.tool.Nullable;
import com.alibaba.cz.base.tool.log.Log;
import com.alibaba.cz.base.tool.page.PageResult;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.lattice2.epoch.sdk.model.Result;
import com.alibaba.lattice2.epoch.user.dto.LoginUser;
import com.alibaba.lattice2.epoch.user.service.EpochLoginUserService;
import com.epoch.app.bcorder.esdegrade.dto.GetEsDegradeValueResponse;
import com.epoch.app.bcorder.esdegrade.service.EsDegradeService;
import com.epoch.app.bcorder.forecast_sales.ForecastOrderTag;
import com.epoch.app.bcorder.model.dto.PageQuerySalesPlanLineList2Request;
import com.epoch.app.bcorder.model.dto.PageQuerySalesPlanLineList2Response;
import com.epoch.app.bcorder.model.dto.SalesPlanLineSDO2;
import com.epoch.app.bcorder.service.SalesPlanService;
import com.epoch.app.crb2btradecenter.model.dto.CountSaleOrderForReportRequest;
import com.epoch.app.crb2btradecenter.model.dto.CountSaleOrderForReportResponse;
import com.epoch.app.crb2btradecenter.service.CrB2bTradeCenterService;
import com.epoch.app.crforecastsales.api.forecastorderquery.dto.QueryForecastActivityDetailsRequest;
import com.epoch.app.crforecastsales.cartonspecsquery.service.CartonSpecsQueryService;
import com.epoch.app.crforecastsales.domian.deallinequeue.model.DealLineQueue;
import com.epoch.app.crforecastsales.dto.request.DealForecastListRequest;
import com.epoch.app.crforecastsales.dto.response.DealForecastOrderResponse;
import com.epoch.app.crforecastsales.enums.AnalyzeReasonsSEO;
import com.epoch.app.crforecastsales.forecastorderlineindex.model.ForecastOrderLineIndex;
import com.epoch.app.crforecastsales.model.dto.*;
import com.epoch.app.crforecastsales.model.enums.ForecastDealStatusSEO;
import com.epoch.app.crforecastsales.model.enums.ForecastDealTypeSEO;
import com.epoch.app.crforecastsales.model.enums.ForecastOrderLogEnum;
import com.epoch.app.crforecastsales.sdo.ForecastOrderLineSDO;
import com.epoch.app.crforecastsales.sdo.ForecastOrderSDO;
import com.epoch.app.crplatformenhance.api.strategyquery.service.StrategyQueryService;
import com.epoch.app.crplatformenhance.enums.DimensionTypeEnum;
import com.epoch.app.crplatformenhance.enums.StrategyTypeEnum;
import com.epoch.app.crplatformenhance.model.dto.*;
import com.epoch.app.crplatformenhance.sdo.CustomerAddressSDO;
import com.epoch.app.crplatformenhance.sdo.DimensionSDO;
import com.epoch.app.crplatformenhance.service.CrPlatformEnhanceService;
import com.epoch.app.crpricecenter.api.creditquery.dto.BatchQueryCreditListRequest;
import com.epoch.app.crpricecenter.api.creditquery.service.CreditQueryService;
import com.epoch.app.crpricecenter.model.dto.CreditInfoSDO;
import com.epoch.app.crpricecenter.model.dto.CreditQueryRequest;
import com.epoch.app.crunifiedinventory.model.dto.ForecastAnalysisInventoryRequest;
import com.epoch.app.crunifiedinventory.model.dto.ForecastAnalysisQueryInventoryRequest;
import com.epoch.app.crunifiedinventory.model.dto.GetListWarehouseStockRequest;
import com.epoch.app.crunifiedinventory.model.dto.WarehouseInventory;
import com.epoch.app.crunifiedinventory.service.CrUnifiedInventoryService;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang.math.NumberUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.math3.util.Pair;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Service
public class ForecastOrderLineAbility {
    private final Log log = Log.getLogger(ForecastOrderLineAbility.class);

    /**
     * 内外码转换 key
     */
    private static String CODE_FLAG = "codeFlag";
    /**
     * 内外码转换 value
     */
    private static String INNER_CODE_TO_OUT_CODE = "inner_code_to_out_code";

    private static final String DISINTEGRATION_QTY = "disintegrationQty";

    @Resource
    private EpochLoginUserService epochLoginUserService;

    @Resource
    private SalesPlanService salesPlanService;

    @Resource
    private EsDegradeService esDegradeService;

    @Resource
    private CrUnifiedInventoryService crUnifiedInventoryService;

    //主单读
    @Autowired
    private ForecastOrderQueryRepository forecastOrderQueryRepository;

    //子单写
    @Autowired
    private ForecastOrderLineWriteRepository forecastOrderLineWriteRepository;

    //子订单读
    @Autowired
    private ForecastOrderLineQueryRepository forecastOrderLineQueryRepository;

    @Autowired
    StrategyQueryService strategyQueryService;
    @Autowired
    SalesPlanLineQueryService salesPlanLineQueryService;

    @Autowired
    CreditQueryService creditQueryService;
    @Autowired
    StrategyCalculate strategyCalculate;
    @Autowired
    private DealLineQueryRepository dealLineQueryRepository;
    @Autowired
    private DealLineWriteRepository dealLineWriteRepository;
    @Autowired
    private SalesPlanQueryService salesPlanQueryService;
    @Autowired
    private CrB2bTradeCenterService crB2bTradeCenterService;

    //预报主订单信息,用于保存预报子订单 的主订单信息，mainOrderId ，ForecastOrderSDO
    private Map<String, ForecastOrderSDO> forecastOrderMap = new HashMap<>();


    @Resource(name = "scItemsQueryTaskExecutor")
    private ThreadPoolTaskExecutor scItemsQueryTaskExecutor;

    @Resource
    private PriceQueryUtils priceQueryUtils;

    @Autowired
    private CartonSpecsQueryService cartonSpecsQueryService;
    @Autowired
    CrPlatformEnhanceService crPlatformEnhanceService;

    public boolean updateReplyQuantity(List<ForecastOrderLineSDO> forecastOrderLineSDOList) {
        boolean isSuccess = true;
        LoginUser loginUser = epochLoginUserService.getLoginUser();
        for (ForecastOrderLineSDO forecastOrderLineSDO : forecastOrderLineSDOList) {
            ForecastOrderLineSDO query = forecastOrderLineQueryRepository.query(forecastOrderLineSDO.getId().toString());
            query.setId(forecastOrderLineSDO.getId());
            query.setPredictReplyAmount(forecastOrderLineSDO.getPredictReplyAmount());
            query.setGoodsProductationTime(forecastOrderLineSDO.getGoodsProductationTime());
            query.setDetailRemark(forecastOrderLineSDO.getDetailRemark());
            forecastOrderLineSDO.setActivityPrice(query.getActivityPrice());
            isSuccess = forecastOrderLineWriteRepository.updateForBatchModify(query);
            //查询库存校验库存是否满足
            try {
                updateCheck(query, null);
            } catch (Exception e) {
                log.error("ERROR:预报订单分析出现问题", e);
                log.error(forecastOrderLineSDO.getId() + "ERROR:预报订单分析出现问题");
//                e.printStackTrace();
            }
        }
        //信贷校验
        //主单ID->主单总计Map
        Map<String,Long> mainTotalAmountMap=new HashMap<>();
        //批量查询信贷入参
        List<CreditQueryRequest> creditQueryRequestList=new ArrayList<>();
        //销售组织ID-客户编码-业务渠道-货品组->信贷余额
        Map<String,Long> creditMap=new HashMap<>();
        //根据主订单进行分组
        Map<String, List<ForecastOrderLineSDO>> mainIdGroupByMap = Nullable.stream(forecastOrderLineSDOList).
                collect(Collectors.groupingBy(ForecastOrderLineSDO::getMainOrderId));
        //主单ID->预报主单Map
        Map<String, ForecastOrderSDO> forecastOrderMap = new HashMap<>();

        for (String mainOrderId : mainIdGroupByMap.keySet()) {
            //查询已经回复订单的总价
            List<ForecastOrderLineSDO> creditLineList = mainIdGroupByMap.get(mainOrderId);

            ForecastOrderSDO forecastOrderInfo = forecastOrderQueryRepository.load(mainOrderId);
            long sum = Nullable.stream(creditLineList).mapToLong(it -> {
                if (Objects.nonNull(it.getUnitPrice())) {
//                    return it.getPredictReplyAmount() * it.getUnitPrice() / 100;
                    //【0102需求】计算订单金额考虑活动价格
                    Long orderLinePrice = Objects.isNull(it.getActivityPrice()) ? it.getUnitPrice() : it.getActivityPrice();
                    orderLinePrice = orderLinePrice <= 0 ? it.getUnitPrice() : orderLinePrice;
                    return it.getPredictReplyAmount() * orderLinePrice ;
                }
                return 0;
            }).sum();
            mainTotalAmountMap.put(mainOrderId, sum);
            forecastOrderMap.put(mainOrderId,forecastOrderInfo);
            CreditQueryRequest creditQueryRequest = ForecastOrderRepositoryConverter.buildCreditQueryRequest(forecastOrderInfo);
            creditQueryRequestList.add(creditQueryRequest);
            LogUtils.printOperateLog(forecastOrderInfo, ForecastOrderLogEnum.EDIT.getForecastOrderLogValue(), "编辑预报订单", loginUser);

        }
        //销售组织ID-客户编码-业务渠道-货品组->预报主单集合
        Map<String, List<ForecastOrderSDO>> customerMainOrderMap = forecastOrderMap.values().stream().collect(Collectors.groupingBy(o ->
                String.format("%s-%s-%s-%s", o.getOrganizationCode(), o.getCustomerCode(), o.getChannelCode(), o.getProductGroupCode())));

        //批量查询信贷
        try {
            batchQueryCreditList(creditQueryRequestList, creditMap);
        } catch (Throwable e) {
            String lastMessage = "信用检查失败";
            log.error("ForecastOrderLineAbility$check_credit_failed", e);
            throw new RuntimeException(lastMessage);
        }
        for (Map.Entry<String, List<ForecastOrderSDO>> customerOrderEntry : customerMainOrderMap.entrySet()) {
            List<ForecastOrderSDO> sameCustomerOrderList = customerOrderEntry.getValue();
            List<ForecastOrderSDO> sortedOrderList = sameCustomerOrderList.stream().sorted(Comparator.comparing(ForecastOrderSDO::getOrderBizId)).collect(Collectors.toList());
            for (ForecastOrderSDO forecastOrderSDO : sortedOrderList) {
                String mainOrderId = forecastOrderSDO.getId();
                Long sum = mainTotalAmountMap.getOrDefault(mainOrderId, 0L);
                String creditKey = String.format("%s-%s-%s-%s", forecastOrderSDO.getOrganizationCode(),  forecastOrderSDO.getCustomerCode(), forecastOrderSDO.getChannelCode(),
                        forecastOrderSDO.getProductGroupCode());
                Long balance = creditMap.get(creditKey);
                if (balance != null ) {
                    if ( sum > balance){
                        //查询已经回复订单的总价
                        List<ForecastOrderLineSDO> creditLineList = mainIdGroupByMap.get(mainOrderId);
                        for (ForecastOrderLineSDO forecastOrderLineSDO : creditLineList) {
                            ForecastOrderLineSDO query = forecastOrderLineQueryRepository.query(String.valueOf(forecastOrderLineSDO.getId()));
                            if (Objects.nonNull(query)) {
                                Map<String, Object> features = query.getFeatures();
                                forecastOrderLineSDO.setAnalysisReason(AnalyzeReasonsSEO.INSUFFICIENT_ACCOUNTS.getName());
                                if (MapUtils.isEmpty(features)) {
                                    features = new HashMap<>();
                                }
                                features.put(ForecastConstants.BULB_TYPE, AnalyzeReasonsSEO.INSUFFICIENT_ACCOUNTS.getBulbType());
                                forecastOrderLineSDO.setFeatures(features);
                                forecastOrderLineSDO.setAnalysisTime(new Date());
                                forecastOrderLineWriteRepository.updateForAnalysis(forecastOrderLineSDO);
                            }
                        }
                    }else {
                        //扣减余额
                        balance=balance-sum;
                        creditMap.put(creditKey,balance);
                    }
                }
            }
        }

        return isSuccess;
    }

    private void batchQueryCreditList(List<CreditQueryRequest> creditQueryRequestList, Map<String, Long> creditMap) {
        //去重同条件信贷查询
        List<CreditQueryRequest> distinctList = creditQueryRequestList.stream().collect(Collectors.collectingAndThen(Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(o -> o.getCustomerCode()+"-"+o.getChannelCode()+"-"+o.getInnerOrganizationCode()+"-"+o.getItemGroupCode()))), ArrayList::new));
        BatchQueryCreditListRequest batchQueryCreditListRequest = new BatchQueryCreditListRequest();
        batchQueryCreditListRequest.setRequestList(distinctList);
        Result<List<CreditInfoSDO>> batchQueryCreditList = creditQueryService.batchQueryCreditList(batchQueryCreditListRequest);

        if (Objects.nonNull(batchQueryCreditList) &&
                CollectionUtils.isNotEmpty(batchQueryCreditList.getResult())) {
            for (CreditInfoSDO creditInfoSDO : batchQueryCreditList.getResult()) {
                if (Objects.nonNull(creditInfoSDO.getBalance())) {
                    String creditKey = String.format("%s-%s-%s-%s", creditInfoSDO.getInnerOrganizationCode(),  creditInfoSDO.getCustomerCode(), creditInfoSDO.getChannelCode(),
                            creditInfoSDO.getItemGroupCode());
                    long balance = new BigDecimal(creditInfoSDO.getBalance()).multiply(new BigDecimal(100)).longValue();
                    creditMap.put(creditKey, balance);
                }
            }
        }
    }

    //TODO：需要进行同步修改
    //更新后进行分析，
    public void updateCheck(ForecastOrderLineSDO forecastOrderLineSDO, AnalyzeProcessDO processDO) {
        //查询主单的数据
        ForecastOrderSDO forecastOrderSDO = forecastOrderQueryRepository.load(forecastOrderLineSDO.getMainOrderId());
        //查询库存的数据
        Double integer = queryGoodsInventory(forecastOrderSDO.getWareHouseCode(), processDO, forecastOrderLineSDO.getScItemId());
        //标记库存是否满足预报回复数量
        if (forecastOrderLineSDO.getPredictReplyAmount() > integer) {
            forecastOrderLineSDO.setAnalysisReason(AnalyzeReasonsSEO.INVENTORY_SHORTAGE.getName());
            if (Objects.isNull(forecastOrderLineSDO.getFeatures())) {
                Map<String, Object> map = new HashMap<>();
                map.put(ForecastConstants.BULB_TYPE, AnalyzeReasonsSEO.INVENTORY_SHORTAGE.getBulbType());
                forecastOrderLineSDO.setFeatures(map);
            } else {
                forecastOrderLineSDO.getFeatures().put(ForecastConstants.BULB_TYPE, AnalyzeReasonsSEO.INVENTORY_SHORTAGE.getBulbType());
            }
            forecastOrderLineSDO.setAnalysisTime(new Date());
            forecastOrderLineWriteRepository.updateForAnalysis(forecastOrderLineSDO);
            return;
        }
        //查询起运量，预报回复数量是否满足起运量
        Integer integer1 = queryInducement(forecastOrderSDO);
        ForecastOrderLineSDO queryForecastLineRequest = new ForecastOrderLineSDO();
        queryForecastLineRequest.setMainOrderId(forecastOrderSDO.getId());
        List<ForecastOrderLineSDO> forecastOrderLineSDOS = forecastOrderLineQueryRepository.queyrLineDetailList(queryForecastLineRequest);
        //变更  查询预报订单主单下面预计回复数量的总和和起运量进行比较
        Integer count = Nullable.stream(forecastOrderLineSDOS).mapToInt(it -> it.getPredictReplyAmount()).sum();
        if (integer1 > count) {//不足起运量
            forecastOrderLineSDO.setAnalysisReason(AnalyzeReasonsSEO.INSUFFICIENT_SHIPMENT.getName());
            if (Objects.isNull(forecastOrderLineSDO.getFeatures())) {
                Map<String, Object> map = new HashMap<>();
                map.put(ForecastConstants.BULB_TYPE, AnalyzeReasonsSEO.INSUFFICIENT_SHIPMENT.getBulbType());
                forecastOrderLineSDO.setFeatures(map);
            } else {
                forecastOrderLineSDO.getFeatures().put(ForecastConstants.BULB_TYPE, AnalyzeReasonsSEO.INSUFFICIENT_SHIPMENT.getBulbType());
            }
            forecastOrderLineSDO.setAnalysisTime(new Date());
            forecastOrderLineWriteRepository.updateForAnalysis(forecastOrderLineSDO);
            return;
        }
        //查询计划量，查询月度计划的数量
        Integer integer3 = strategyCalculate.pagePlanNumber(forecastOrderLineSDO);
        if (integer3 != (-1) && integer3 < forecastOrderLineSDO.getPredictReplyAmount()) {
            forecastOrderLineSDO.setAnalysisReason(AnalyzeReasonsSEO.OVER_MONTH_VOLUME.getName());
            if (Objects.isNull(forecastOrderLineSDO.getFeatures())) {
                Map<String, Object> map = new HashMap<>();
                map.put(ForecastConstants.BULB_TYPE, AnalyzeReasonsSEO.OVER_MONTH_VOLUME.getBulbType());
                forecastOrderLineSDO.setFeatures(map);
            } else {
                forecastOrderLineSDO.getFeatures().put(ForecastConstants.BULB_TYPE, AnalyzeReasonsSEO.OVER_MONTH_VOLUME.getBulbType());
            }
            forecastOrderLineSDO.setAnalysisTime(new Date());
            forecastOrderLineWriteRepository.updateForAnalysis(forecastOrderLineSDO);
            return;
        }//查询信贷
//        CreditQueryRequest creditQueryRequest = new CreditQueryRequest();
//        creditQueryRequest.setOrganizationCode(forecastOrderSDO.getOrganizationCode());
//        creditQueryRequest.setChannelCode(forecastOrderSDO.getChannelCode());
//        creditQueryRequest.setItemGroupCode(forecastOrderSDO.getProductGroupCode());
//        creditQueryRequest.setCustomerCode(forecastOrderSDO.getCustomerCode());
//        Result<CreditInfoSDO> creditReponseResult = creditQueryService.queryCreditBalance(creditQueryRequest);
//        Long balance = 0L;
//        if (Objects.nonNull(creditReponseResult) && Objects.nonNull(creditReponseResult.getResult()) && Objects.nonNull(creditReponseResult.getResult().getBalance())) {
//            balance = MoneyUtil.formatRoundLong(creditReponseResult.getResult().getBalance());
//        }
        //查询订单总金额
//        long sum = Nullable.stream(forecastOrderLineSDOS).mapToLong(it -> {
//            if (Objects.nonNull(it.getUnitPrice())) {
//                return it.getPredictReplyAmount() * it.getUnitPrice();
//            }
//            return 0;
//        }).sum();
        //比较客户信贷与总金额的大小，若信贷金额没有查到就不校验
//        if (balance != 0 && sum > balance) {
//            forecastOrderLineSDO.setAnalysisReason(AnalyzeReasonsSEO.INSUFFICIENT_ACCOUNTS.getName());
//            if (Objects.isNull(forecastOrderLineSDO.getFeatures())) {
//                Map<String, Object> map = new HashMap<>();
//                map.put(ForecastConstants.BULB_TYPE, AnalyzeReasonsSEO.INSUFFICIENT_ACCOUNTS.getBulbType());
//                forecastOrderLineSDO.setFeatures(map);
//            } else {
//                forecastOrderLineSDO.getFeatures().put(ForecastConstants.BULB_TYPE, AnalyzeReasonsSEO.INSUFFICIENT_ACCOUNTS.getBulbType());
//            }
//            forecastOrderLineSDO.setAnalysisTime(new Date());
//            forecastOrderLineWriteRepository.updateForAnalysis(forecastOrderLineSDO);
//            return;
//        }
        //若回复差量小于预计回复数量，完全满足
        if (forecastOrderLineSDO.getPredictReplyAmount() >= Integer.parseInt(forecastOrderLineSDO.getQuantity()) - (forecastOrderLineSDO.getActualTotalReplyAmount() != null ? forecastOrderLineSDO.getActualTotalReplyAmount() : 0)) {
            forecastOrderLineSDO.setAnalysisReason(AnalyzeReasonsSEO.TOTALLY_SATISFIED.getName());
            if (Objects.isNull(forecastOrderLineSDO.getFeatures())) {
                Map<String, Object> map = new HashMap<>();
                map.put(ForecastConstants.BULB_TYPE, AnalyzeReasonsSEO.TOTALLY_SATISFIED.getBulbType());
                forecastOrderLineSDO.setFeatures(map);
            } else {
                forecastOrderLineSDO.getFeatures().put(ForecastConstants.BULB_TYPE, AnalyzeReasonsSEO.TOTALLY_SATISFIED.getBulbType());
            }
            forecastOrderLineSDO.setAnalysisTime(new Date());
            forecastOrderLineWriteRepository.updateForAnalysis(forecastOrderLineSDO);
            return;
        }
        //若回复差量大于预计回复数量，改为分析原因库存不足
        if (forecastOrderLineSDO.getPredictReplyAmount() < Integer.parseInt(forecastOrderLineSDO.getQuantity()) - (forecastOrderLineSDO.getActualTotalReplyAmount() != null ? forecastOrderLineSDO.getActualTotalReplyAmount() : 0)) {
            forecastOrderLineSDO.setAnalysisReason(AnalyzeReasonsSEO.INVENTORY_SHORTAGE.getName());
            if (Objects.isNull(forecastOrderLineSDO.getFeatures())) {
                Map<String, Object> map = new HashMap<>();
                map.put(ForecastConstants.BULB_TYPE, AnalyzeReasonsSEO.INVENTORY_SHORTAGE.getBulbType());
                forecastOrderLineSDO.setFeatures(map);
            } else {
                forecastOrderLineSDO.getFeatures().put(ForecastConstants.BULB_TYPE, AnalyzeReasonsSEO.INVENTORY_SHORTAGE.getBulbType());
            }
            forecastOrderLineSDO.setAnalysisTime(new Date());
            forecastOrderLineWriteRepository.updateForAnalysis(forecastOrderLineSDO);
        }
    }

    public List<DealForecastOrderResponse> loadDealForecastList(DealForecastListRequest dealForecastListRequest) {
        //根据当前日期的单据主单Id 查询出所有的子单
        List<String> nowLineIds = dealForecastListRequest.getNowLineIds();

        // 统计当天累计剩余回复量
        Map<String, Integer> remainReplyCntMap = new HashMap<>();
        if (CollectionUtils.isNotEmpty(nowLineIds)) {
            //获取当天创建的所有子订单
            PageResult<List<ForecastOrderLineSDO>> forecastOrderLineSDOResult = forecastOrderLineQueryRepository.batchLoadList(nowLineIds);
            List<ForecastOrderLineSDO> forecastOrderLineSDOS = forecastOrderLineSDOResult.getContent();

            // 按照货品进行分组
            for (ForecastOrderLineSDO forecastOrderLineSDO : forecastOrderLineSDOS) {
                // 货品ID
                String scItemId = forecastOrderLineSDO.getScItemId();
                // 累计回复量
                Integer actualTotalReplyAmount = Optional.ofNullable(forecastOrderLineSDO.getActualTotalReplyAmount()).orElse(0);
                // 剩余回复量
                Integer remainQuantity = Integer.parseInt(forecastOrderLineSDO.getQuantity()) - actualTotalReplyAmount;
                if (remainReplyCntMap.containsKey(scItemId)) {
                    int totalRemainQuantity = remainReplyCntMap.get(scItemId) + remainQuantity;
                    remainReplyCntMap.put(scItemId, totalRemainQuantity);
                } else {
                    remainReplyCntMap.put(scItemId, remainQuantity);
                }
            }
        }

        Set<String> orderIds = Sets.newHashSet();
        // 查询子单列表
        List<String> orderLineIds = new ArrayList<>();
        dealForecastListRequest.getIds().forEach(id -> orderLineIds.add(String.valueOf(id)));
        PageResult<List<ForecastOrderLineSDO>> forecastOrderLineSDOListResult = forecastOrderLineQueryRepository.batchLoadList(orderLineIds);
        List<ForecastOrderLineSDO> forecastOrderLineSDOList = forecastOrderLineSDOListResult.getContent();
        for (ForecastOrderLineSDO forecastOrderLineSDO : forecastOrderLineSDOList) {
            orderIds.add(forecastOrderLineSDO.getMainOrderId());
        }

        // 批量获取主单 & 客户信贷信息
        Map<String, ForecastOrderSDO> mainOrderMap = Maps.newConcurrentMap();
        Map<String, Long> mainOrderCreditMap = Maps.newConcurrentMap();
        List<CompletableFuture<Pair<String, Long>>> creditFeatures = Lists.newCopyOnWriteArrayList();
        List<ForecastOrderSDO> mainOrderList = forecastOrderQueryRepository.batchQueryForecastOrder(Lists.newArrayList(orderIds));
        for (ForecastOrderSDO forecastOrderSDO : mainOrderList) {
            if (mainOrderMap.containsKey(forecastOrderSDO.getId())) {
                continue;
            }

            mainOrderMap.put(forecastOrderSDO.getId(), forecastOrderSDO);

            // TODO: 2021/12/23 优化信贷查询的性能
            CompletableFuture<Pair<String, Long>> future = CompletableFuture.supplyAsync(() -> {
                String organizationCode = forecastOrderSDO.getOrganizationCode();
                String customerCode = forecastOrderSDO.getCustomerCode();
                String channelCode = forecastOrderSDO.getChannelCode();
                String productGroupCode = forecastOrderSDO.getProductGroupCode();

                String creditKey = String.format("%s-%s-%s-%s", organizationCode, customerCode, channelCode, productGroupCode);

                CreditQueryRequest req = new CreditQueryRequest();
                req.setOrganizationCode(organizationCode);
                req.setChannelCode(channelCode);
                req.setItemGroupCode(productGroupCode);
                req.setCustomerCode(customerCode);
                Long creditCnt = queryCustomerCredit(req);

                return Pair.create(creditKey, creditCnt);

            }, scItemsQueryTaskExecutor);

            creditFeatures.add(future);
        }

        // 按照仓库分组货品
        Map<String, Set<String>> itemWarehoseGroup = Maps.newHashMap();
        for (ForecastOrderLineSDO forecastOrderLineSDO : forecastOrderLineSDOList) {
            // 按照仓库将货品分组
            String mainWareHouseCode = mainOrderMap.get(forecastOrderLineSDO.getMainOrderId()).getWareHouseCode();
            String subScItemId = forecastOrderLineSDO.getScItemId();

            Set<String> scItemSets = itemWarehoseGroup.get(mainWareHouseCode);
            if (scItemSets == null) {
                scItemSets = Sets.newHashSet();
                itemWarehoseGroup.put(mainWareHouseCode, scItemSets);
            }
            scItemSets.add(subScItemId);
        }

        // 批量查询库存
        Map<String, Double> inventoryMap = Maps.newHashMap();
        for (Map.Entry<String, Set<String>> entrySet : itemWarehoseGroup.entrySet()) {
            // 获取可用库存
            GetListWarehouseStockRequest getListWarehouseStockRequest = GetListWarehouseStockRequest.builder()
                    .goodsIds(Lists.newArrayList(entrySet.getValue()))
                    .type("1")
                    .warehouseAreaId(entrySet.getKey())
                    .size(entrySet.getValue().size())
                    .page(1).build();

            List<WarehouseInventory> warehouseStockList = forecastOrderQueryRepository.getListWarehouseStock(getListWarehouseStockRequest);
            if (org.apache.commons.collections.CollectionUtils.isEmpty(warehouseStockList)) {
                continue;
            }

            for (WarehouseInventory warehouseInventory : warehouseStockList) {
                String inventoryKey = String.format("%s-%s", warehouseInventory.getWarehouseAreaId(), warehouseInventory.getGoodsId());
                inventoryMap.put(inventoryKey, Double.parseDouble(warehouseInventory.getAvailableQuantityFormat()));
            }
        }

        // 获取信贷信息
        for (CompletableFuture<Pair<String, Long>> creditFeature : creditFeatures) {
            Pair<String, Long> credit = null;
            try {
                // 信贷接口设置1秒的超时时间， 失败直接降级掉
                credit = creditFeature.get(2, TimeUnit.SECONDS);
            } catch (Exception e) {
                log.error("信贷查询失败", e);
            }

            if (credit != null && credit.getKey() != null && credit.getValue() != null) {
                mainOrderCreditMap.put(credit.getKey(), credit.getValue());
            }
        }

        List<DealForecastOrderResponse> dealForecastOrderResponses = new CopyOnWriteArrayList<>();
        Map<Long, DealForecastOrderResponse> dealForecastOrderResponseMap = new ConcurrentHashMap<>();
        Map<Long, CompletableFuture<Integer>> dealForecastOrderResponsesFutures = new ConcurrentHashMap<>();
        for (ForecastOrderLineSDO forecastOrderLineSDO : forecastOrderLineSDOList) {
            ForecastOrderSDO forecastOrderSDO = mainOrderMap.get(forecastOrderLineSDO.getMainOrderId());
            String inventoryKey = String.format("%s-%s", forecastOrderSDO.getWareHouseCode(), forecastOrderLineSDO.getScItemId());
            Integer remainCnt = remainReplyCntMap.get(forecastOrderLineSDO.getScItemId());
            Double inventoryCnt = inventoryMap.get(inventoryKey);

            String organizationCode = forecastOrderSDO.getOrganizationCode();
            String customerCode = forecastOrderSDO.getCustomerCode();
            String channelCode = forecastOrderSDO.getChannelCode();
            String productGroupCode = forecastOrderSDO.getProductGroupCode();
            String creditKey = String.format("%s-%s-%s-%s", organizationCode, customerCode, channelCode, productGroupCode);
            Long creditCnt = mainOrderCreditMap.get(creditKey);
            DealForecastOrderResponse dealForecastOrderResponse = convert2DealForecastOrderResponse(forecastOrderLineSDO, forecastOrderSDO, remainCnt, inventoryCnt, creditCnt);
            dealForecastOrderResponses.add(dealForecastOrderResponse);
            dealForecastOrderResponseMap.put(forecastOrderLineSDO.getId(), dealForecastOrderResponse);
            CompletableFuture<Integer> dealForecastOrderResponseFuture = CompletableFuture.supplyAsync(() -> {
                //计划差量
                /**
                 * 1、获取该客户的本月累计月度计划回复数量
                 * 2、计算本月销售订单的累计出库数量
                 * 3、该值=1-2
                 * 4、不可编辑
                 */
                //根据固定条件获取客户地址唯一id
                //计划量和发货数量的差量
                Integer planDifferenceQuantity = getPlanDifferenceQuantity(forecastOrderLineSDO, forecastOrderSDO);
                return planDifferenceQuantity;
            }, scItemsQueryTaskExecutor);

            dealForecastOrderResponsesFutures.put(forecastOrderLineSDO.getId(), dealForecastOrderResponseFuture);
        }

        for (Map.Entry<Long, CompletableFuture<Integer>> entry : dealForecastOrderResponsesFutures.entrySet()) {
            DealForecastOrderResponse resp = dealForecastOrderResponseMap.get(entry.getKey());
            Integer planVariance = null;
            try {
                planVariance = entry.getValue().get(2, TimeUnit.SECONDS);
            } catch (Exception e) {
                log.info("获取计划差量失败", e);
            }
            resp.setPlanVariance(planVariance);
        }


        return dealForecastOrderResponses;
    }

    @NotNull
    private DealForecastOrderResponse convert2DealForecastOrderResponse(ForecastOrderLineSDO forecastOrderLineSDO, ForecastOrderSDO forecastOrderSDO, Integer remainCnt, Double inventoryCnt, Long creditCnt) {
        if (forecastOrderSDO == null || forecastOrderLineSDO == null) {
            return null;
        }

        DealForecastOrderResponse dealForecastOrderResponse = new DealForecastOrderResponse();
        //销售组织编码
        dealForecastOrderResponse.setOrganizationCode(forecastOrderSDO.getOrganizationCode());
        //分销渠道
        dealForecastOrderResponse.setSaleChannelCode(forecastOrderSDO.getSaleChannelCode());
        //产品组
        dealForecastOrderResponse.setProductGroupCode(forecastOrderSDO.getProductGroupCode());
        //售达方
        dealForecastOrderResponse.setCustomerCode(forecastOrderSDO.getCustomerCode());
        //明细id
        dealForecastOrderResponse.setId(String.valueOf(forecastOrderLineSDO.getId()));
        //主单id
        dealForecastOrderResponse.setMainOrderId(forecastOrderSDO.getId());
        //预计恢复数量
        dealForecastOrderResponse.setPredictReplyAmount(forecastOrderLineSDO.getPredictReplyAmount());
        //客户名称
        dealForecastOrderResponse.setCustomerName(forecastOrderSDO.getCustomerName());
        //分析原因
        dealForecastOrderResponse.setAnalysisReason(forecastOrderLineSDO.getAnalysisReason());
        //商品名称
        dealForecastOrderResponse.setScItemTitle(forecastOrderLineSDO.getScItemTitle());
        //商品名称
        dealForecastOrderResponse.setScItemName(forecastOrderLineSDO.getScItemTitle());
        //行号
        dealForecastOrderResponse.setSerialNumber(forecastOrderLineSDO.getSerialNumber());
        //获取可用库存 TODO 需要调用接口查询
//            QueryAvailableInventoryRequest queryAvailableInventoryRequest = new QueryAvailableInventoryRequest();
//            queryAvailableInventoryRequest.setType("1");
//            queryAvailableInventoryRequest.setWarehouseAreaId(forecastOrderSDO.getWareHouseCode());
//            queryAvailableInventoryRequest.setGoodsId(forecastOrderLineSDO.getScItemId());
//            Double availableQuantity = forecastOrderQueryRepository.queryInventory(queryAvailableInventoryRequest);
        // 查询可用库存
        Double availableQuantity = Optional.ofNullable(inventoryCnt).orElse(0D);
        dealForecastOrderResponse.setInventory(BigDecimal.valueOf(availableQuantity).setScale(3, RoundingMode.FLOOR).toString());
        //orderNo 订单号
        dealForecastOrderResponse.setOrderNo(forecastOrderSDO.getId());
        //预报数量
        dealForecastOrderResponse.setQuantity(forecastOrderLineSDO.getQuantity());
        //actualTotalReplyAmount 累计回复数量
        dealForecastOrderResponse.setActualTotalReplyAmount(forecastOrderLineSDO.getActualTotalReplyAmount());
        //promotionId 促销Id
        dealForecastOrderResponse.setPromotionId(forecastOrderLineSDO.getActivityCode());
        //promotionDesc 促销描述
        dealForecastOrderResponse.setPromotionDesc(forecastOrderLineSDO.getActivityName());
        //unitPrice 单价
        dealForecastOrderResponse.setUnitPrice(forecastOrderLineSDO.getUnitPrice());
        //orderTotalFee 总金额
        dealForecastOrderResponse.setOrderTotalFee(forecastOrderLineSDO.getTotalFee());
        //salesOrderId 销售单号
        dealForecastOrderResponse.setSalesOrderId(forecastOrderSDO.getOuterOrderId());
        //消息说明
        dealForecastOrderResponse.setMessageIndicate("销售订单生成成功");
        //备注
        dealForecastOrderResponse.setDetailRemark(forecastOrderLineSDO.getDetailRemark());
        //信贷余额
        /**
         * 调用SAP信贷查询接口实时获取，不可编辑
         */
        if (creditCnt != null) {
            dealForecastOrderResponse.setCreditBalance(creditCnt);
        }

        //扣减预报可用库存
        /**
         * 1、调用可用库存查询API获取可用库存
         * 2、计算当前日期预报订单的累计未回复说量
         * 3、该值=1-2
         * 4、不可编辑
         */
        //获取当天已经使用的货品数量
        //通过接口获取每日库存减去已经使用的库存
        if (null != remainCnt) {
            Double deductionInventory = availableQuantity - remainCnt;
            dealForecastOrderResponse.setDeductionInventory(BigDecimal.valueOf(deductionInventory).setScale(3, RoundingMode.FLOOR).toString());
        } else {
            dealForecastOrderResponse.setDeductionInventory(BigDecimal.valueOf(availableQuantity).setScale(3, RoundingMode.FLOOR).toString());
        }

        /**
         * 设置业务单id
         */
        dealForecastOrderResponse.setOrderBizId(forecastOrderSDO.getOrderBizId());
        /**
         * 货品生产日期
         */
        dealForecastOrderResponse.setGoodsProductationTime(forecastOrderLineSDO.getGoodsProductationTime());
        /**
         * 拒绝原因
         */
        dealForecastOrderResponse.setRefuseReason(forecastOrderLineSDO.getRefuseReason());
        return dealForecastOrderResponse;
    }

    private Integer getPlanDifferenceQuantity(ForecastOrderLineSDO forecastOrderLineSDO, ForecastOrderSDO forecastOrderSDO) {
        int planDifferenceQuantity = 0;
        try {
            /**
             * 查询客户地址Id
             */
            PageQueryAddressListRequest pageQueryAddressListRequest = PageQueryAddressListRequest.builder()
                    .addressCode(forecastOrderSDO.getReceiveAddressCode())
                    .channelCode(forecastOrderSDO.getChannelCode())
                    .customerCode(forecastOrderSDO.getCustomerCode())
                    .orgSaleOrganizationCode(forecastOrderSDO.getOrganizationCode())
                    .itemGroup(forecastOrderSDO.getProductGroupCode())
                    .build();
            String addressSDOId = "";
            log.info("查询客户地址Id参数", pageQueryAddressListRequest);
            PageQueryAddressListResponse pageQueryAddressListResponse = crPlatformEnhanceService.pageQueryAddressList(pageQueryAddressListRequest);
            if (Objects.nonNull(pageQueryAddressListResponse) && CollectionUtils.isNotEmpty(pageQueryAddressListResponse.getResult())) {
                List<CustomerAddressSDO> result1 = pageQueryAddressListResponse.getResult();
                CustomerAddressSDO customerAddressSDO = result1.get(0);
                addressSDOId = customerAddressSDO.getId();
            } else {
                throw new FunctionException("OTS-04-001-00-15-034");
            }


            /**
             * 查询月度计划planId
             */
            PageQuerySalePlanRequest pageQuerySalePlanRequest = new PageQuerySalePlanRequest();
            /*String date = com.alibaba.citrus.cr.common.util.DateUtils.formatDate(new Date(), "yyyy-MM-dd");
            pageQuerySalePlanRequest.setPlanTime(date);*/
            Calendar cal = Calendar.getInstance();
            int year = cal.get(Calendar.YEAR);
            int month = cal.get(Calendar.MONTH) + 1;
            pageQuerySalePlanRequest.setPlanTime(year + "-" + month);
            pageQuerySalePlanRequest.setOrgBusinessUnitCode(forecastOrderSDO.getBisUnitCode());
            pageQuerySalePlanRequest.setLimit(1);
            String planId = "";
            log.info("查询月度计划PlanId参数", pageQuerySalePlanRequest);
            Result<PageResult<List<SalesPlanSDO>>> pageResultResult = salesPlanQueryService.pageQuerySalesPlanList(pageQuerySalePlanRequest);
            if (Objects.nonNull(pageResultResult) && Objects.nonNull(pageResultResult.getResult()) &&
                    CollectionUtils.isNotEmpty(pageResultResult.getResult().getContent())) {
                PageResult<List<SalesPlanSDO>> result = pageResultResult.getResult();
                List<SalesPlanSDO> content = result.getContent();
                SalesPlanSDO salesPlanSDO = content.get(0);
                planId = salesPlanSDO.getId() == null ? null : String.valueOf(salesPlanSDO.getId());
            } else {
                throw new FunctionException("OTS-04-001-00-15-034");
            }

            /**
             * 获取计划明细回复数量
             */
            PageQuerySalesPlanLineList2Request pageQuerySalePlanRequestLine = new PageQuerySalesPlanLineList2Request();
            pageQuerySalePlanRequestLine.setPlanId(planId);
            pageQuerySalePlanRequestLine.setCustomerAddressId(addressSDOId);//"11"
            pageQuerySalePlanRequestLine.setCustomerCode(forecastOrderSDO.getCustomerCode());//"3730000712");
            pageQuerySalePlanRequestLine.setScItemId(forecastOrderLineSDO.getScItemId());//"306188816041963901");
            pageQuerySalePlanRequestLine.setStart(0);
            pageQuerySalePlanRequestLine.setLimit(10);
            Integer totalPlanQty = 0;
            log.info("查询月度计划明细回复量入参：", pageQuerySalePlanRequestLine);
            PageQuerySalesPlanLineList2Response pageResultResult1 = salesPlanService.pageQuerySalesPlanLineList2(pageQuerySalePlanRequestLine);

            if (Objects.nonNull(pageResultResult1) && Objects.nonNull(pageResultResult1.getResult()) &&
                    CollectionUtils.isNotEmpty(pageResultResult1.getResult())) {
                List<SalesPlanLineSDO2> salesPlanLineSDOS = pageResultResult1.getResult();
                SalesPlanLineSDO2 salesPlanLineSDO = salesPlanLineSDOS.get(0);
                totalPlanQty += salesPlanLineSDO.getMonthFirstReturnCount();
                totalPlanQty += salesPlanLineSDO.getMonthMiddleReturnCount();
                totalPlanQty += salesPlanLineSDO.getMonthLastReturnCount();
            } else {
                throw new FunctionException("OTS-04-001-00-15-034");
            }

            CountSaleOrderForReportRequest countSaleOrderForReportRequest = new CountSaleOrderForReportRequest();
            countSaleOrderForReportRequest.setGmtCreateStartTime(DateUtils.firstDayOfMonth());
            countSaleOrderForReportRequest.setGmtCreateEndTime(DateUtils.endDayOfMonth());
            countSaleOrderForReportRequest.setDetailStatusList(Arrays.asList(3, 4));
            countSaleOrderForReportRequest.setScItemId(forecastOrderLineSDO.getScItemId());
            log.info("查询统计销售子单发货数入参：", countSaleOrderForReportRequest);
            CountSaleOrderForReportResponse countSaleOrderForReportResponse = crB2bTradeCenterService.countSaleOrderForReport(countSaleOrderForReportRequest);
            if (Objects.nonNull(countSaleOrderForReportResponse)) {
                planDifferenceQuantity = (totalPlanQty - (int) Float.parseFloat(countSaleOrderForReportResponse.getDeliveryAmount()));
                if (planDifferenceQuantity < 0) {
                    planDifferenceQuantity = 0;
                }
            } else {
                throw new FunctionException("OTS-04-001-00-15-034");
            }
        } catch (Exception e) {
            planDifferenceQuantity = -1;
            log.error("ERROR:计划差量查询失败", e);
        }
        return planDifferenceQuantity;
    }

    private Long queryCustomerCredit(CreditQueryRequest request) {
        try {
            Result<CreditInfoSDO> result = creditQueryService.queryCreditBalance(request);
            CreditInfoSDO response = result.getResult();
            if (result.isSuccess() || response != null) {
                return MoneyUtil.formatRoundLong(response.getBalance());
            }
        } catch (Exception e) {
            log.error("ERROR:获取信贷余额失败 req:" + JSON.toJSONString(request), e);
        }
        return null;
    }

    @ProcessInvoker
    public List<ForecastOrderActivitiesResponse> queryActivity(ForecastOrderActivityRequest forecastOrderActivityRequest) {
        if (CollectionUtils.isEmpty(forecastOrderActivityRequest.getIds())){
            return new ArrayList<>();
        }
        List<ForecastOrderActivitiesResponse> forecastOrderActivityResponses = new ArrayList<>();
        List<ForecastOrderLineSDO> forecastOrderLineSDOList = forecastOrderLineQueryRepository.batchForecastOrderLineList(forecastOrderActivityRequest.getIds());
        //根据主单id进行分组
        Map<String, List<ForecastOrderLineSDO>> forecastOrderlineMap = Nullable.stream(forecastOrderLineSDOList).collect(Collectors.groupingBy(ForecastOrderLineSDO::getMainOrderId));
        for (String key : forecastOrderlineMap.keySet()) {
            ForecastOrderSDO forecastOrderSDO = forecastOrderQueryRepository.load(key);
            List<ForecastOrderLineSDO> forecastOrderLineSDOS = forecastOrderlineMap.get(key);
            //查询命中活动详情
            List<ActivitySDO> activitySDOS = queryActivity(forecastOrderLineSDOS, forecastOrderSDO);
            if (CollectionUtils.isNotEmpty(activitySDOS)) {
                Map<String, List<ActivitySDO>> activitySDOMap = activitySDOS.stream().collect(Collectors.groupingBy(ActivitySDO::getSubOrderId));
                for (String subId : activitySDOMap.keySet()) {
                    ForecastOrderLineSDO forecastOrderLineSDO = forecastOrderLineQueryRepository.query(subId);
                    List<ActivitySDO> lineActivitySDOS = activitySDOMap.get(subId);
                    //构造返回参数
                    ForecastOrderActivitiesResponse forecastOrderActivityResponse = createForecastOrderActivityResponse(lineActivitySDOS, forecastOrderLineSDO, forecastOrderSDO);
                    forecastOrderActivityResponses.add(forecastOrderActivityResponse);
                }
            }
        }
        return forecastOrderActivityResponses;
    }

    @ProcessInvoker
    public List<ForecastOrderActivityResponse> queryActivityObsolete(ForecastOrderActivityRequest forecastOrderActivityRequest) {
        if (CollectionUtils.isEmpty(forecastOrderActivityRequest.getIds())){
            return new ArrayList<>();
        }
        List<ForecastOrderActivityResponse> forecastOrderActivityResponses = new ArrayList<>();
        List<ForecastOrderLineSDO> forecastOrderLineSDOList = forecastOrderLineQueryRepository.batchForecastOrderLineList(forecastOrderActivityRequest.getIds());
        //根据主单id进行分组
        Map<String, List<ForecastOrderLineSDO>> forecastOrderlineMap = Nullable.stream(forecastOrderLineSDOList).collect(Collectors.groupingBy(ForecastOrderLineSDO::getMainOrderId));
        for (String key : forecastOrderlineMap.keySet()) {
            ForecastOrderSDO forecastOrderSDO = forecastOrderQueryRepository.load(key);
            List<ForecastOrderLineSDO> forecastOrderLineSDOS = forecastOrderlineMap.get(key);
            //查询命中活动详情
            List<ActivitySDO> activitySDOS = queryActivity(forecastOrderLineSDOS, forecastOrderSDO);
            if (CollectionUtils.isNotEmpty(activitySDOS)) {
                Map<String, List<ActivitySDO>> activitySDOMap = activitySDOS.stream().collect(Collectors.groupingBy(ActivitySDO::getSubOrderId));
                for (String subId : activitySDOMap.keySet()) {
                    ForecastOrderLineSDO forecastOrderLineSDO = forecastOrderLineQueryRepository.query(subId);
                    List<ActivitySDO> activitySDOS1 = activitySDOMap.get(subId);
                    //构造返回参数
                    ForecastOrderActivityResponse forecastOrderActivityResponse = createForecastOrderActivityResponseObsolete(activitySDOS1, forecastOrderLineSDO, forecastOrderSDO);
                    forecastOrderActivityResponses.add(forecastOrderActivityResponse);
                }
            }
        }
        return forecastOrderActivityResponses;
    }

    private ForecastOrderActivitiesResponse createForecastOrderActivityResponse(List<ActivitySDO> activitySDOS, ForecastOrderLineSDO forecastOrderLineSDO, ForecastOrderSDO forecastOrderSDO) {
        Map<String, List<ActivitySDO>> activityTypeMap = new HashMap<>();
        ForecastOrderActivitiesResponse forecastOrderActivityResponse = new ForecastOrderActivitiesResponse();
        if (CollectionUtils.isNotEmpty(activitySDOS)) {
            Nullable.stream(activitySDOS).forEach(o->o.setUnitPrice(forecastOrderLineSDO.getUnitPrice()));
            activityTypeMap = Nullable.stream(activitySDOS).collect(Collectors.groupingBy(ActivitySDO::getActivityType));
        }
        List<ActivitySDO> activityChooseList;
        Map<String,Object> features = forecastOrderLineSDO.getFeatures();
        String activityTag = MapUtils.isNotEmpty(features) ? String.valueOf(features.getOrDefault(ForecastConstants.ACTIVITY_TAG, "0")) : "0";
        if (MapUtils.isNotEmpty(activityTypeMap)) {

            activityChooseList = activityTypeMap.get("ZS42");

            if (CollectionUtils.isEmpty(activityChooseList)) {
                if (CollectionUtils.isNotEmpty(activityTypeMap.get("ZS02"))){
                    List<ActivitySDO> giftActivitySDOList = activityTypeMap.get("ZS02");
                    boolean haveSelectedActivity = giftActivitySDOList.stream().anyMatch(ActivitySDO::getIsSelect);
                    ActivityDTO activityDTO=new ActivityDTO();
                    activityDTO.setList(giftActivitySDOList);
                    activityDTO.setEnabled(haveSelectedActivity);
                    activityDTO.setActivityType("ZS02");
                    if (haveSelectedActivity){
                        ActivitySDO selectActivitySDO = giftActivitySDOList.stream().filter(ActivitySDO::getIsSelect).findAny().orElse(new ActivitySDO());
                        activityDTO.setSelectedActivityCode(selectActivitySDO.getActivityCode());
                    }
                    forecastOrderActivityResponse.setGiftActivity(activityDTO);
                    //默认选中第一个(分析完成以后，初次查询活动)
                    if (activityTag.equals("1")){
                        ActivitySDO defaultSelectActivity = giftActivitySDOList.get(0);
                        activityDTO.setEnabled(Boolean.TRUE);
                        activityDTO.setSelectedActivityCode(defaultSelectActivity.getActivityCode());

                    }
                }
            }else {
                    boolean haveSelectedActivity = activityChooseList.stream().anyMatch(ActivitySDO::getIsSelect);
                    ActivityDTO activityDTO=new ActivityDTO();
                    activityDTO.setList(activityChooseList);
                    activityDTO.setEnabled(haveSelectedActivity);
                    activityDTO.setActivityType("ZS42");
                    if (haveSelectedActivity){
                        ActivitySDO selectActivitySDO = activityChooseList.stream().filter(ActivitySDO::getIsSelect).findAny().orElse(new ActivitySDO());
                        activityDTO.setSelectedActivityCode(selectActivitySDO.getActivityCode());
                    }
                    forecastOrderActivityResponse.setGiftActivity(activityDTO);
            }


            if (CollectionUtils.isNotEmpty(activityTypeMap.get("ZS03"))){
                List<ActivitySDO> giftActivitySDOList = activityTypeMap.get("ZS03");
                boolean haveSelectedActivity = giftActivitySDOList.stream().anyMatch(ActivitySDO::getIsSelect);
                ActivityDTO activityDTO=new ActivityDTO();
                activityDTO.setList(giftActivitySDOList);
                activityDTO.setEnabled(haveSelectedActivity);
                activityDTO.setActivityType("ZS03");
                if (haveSelectedActivity){
                    ActivitySDO selectActivitySDO = giftActivitySDOList.stream().filter(ActivitySDO::getIsSelect).findAny().orElse(new ActivitySDO());
                    activityDTO.setSelectedActivityCode(selectActivitySDO.getActivityCode());
                }
                forecastOrderActivityResponse.setSpecialActivity(activityDTO);
                //默认选中第一个(分析完成以后，初次查询活动)
                if (activityTag.equals("1")){
                    ActivitySDO defaultSelectActivity = giftActivitySDOList.get(0);
                    activityDTO.setEnabled(Boolean.TRUE);
                    activityDTO.setSelectedActivityCode(defaultSelectActivity.getActivityCode());

                }
            }


            //设置预报订单id
            forecastOrderActivityResponse.setId(forecastOrderSDO.getId());
            //设置预报业务id
            forecastOrderActivityResponse.setOrderBizId(forecastOrderSDO.getOrderBizId());
//            //设置选中的活动id
//            List<ChooseActivityResponse> activitySelectList = new ArrayList<>();

            //货品id
            forecastOrderActivityResponse.setScItemId(forecastOrderLineSDO.getScItemId());
            //货品名称
            forecastOrderActivityResponse.setScItemName(forecastOrderLineSDO.getScItemTitle());
            //行号
            forecastOrderActivityResponse.setLineNo(String.valueOf(forecastOrderLineSDO.getSerialNumber()));
            //客户名称
            forecastOrderActivityResponse.setCustomerName(forecastOrderSDO.getCustomerName());
            //预报子单id
            forecastOrderActivityResponse.setForecastOrderLineId(String.valueOf(forecastOrderLineSDO.getId()));
            //predictReplyAmount 预计回复数量
            forecastOrderActivityResponse.setPredictReplyAmount(forecastOrderLineSDO.getPredictReplyAmount());
            //活动单价备注
            forecastOrderActivityResponse.setActivityPriceRemark(forecastOrderLineSDO.getActivityPriceRemark());
            //明细备注
            forecastOrderActivityResponse.setDetailRemark(forecastOrderLineSDO.getDetailRemark());

        }
        return forecastOrderActivityResponse;
    }

    private ForecastOrderActivityResponse createForecastOrderActivityResponseObsolete(List<ActivitySDO> activitySDOS, ForecastOrderLineSDO forecastOrderLineSDO, ForecastOrderSDO forecastOrderSDO) {
        Map<String, List<ActivitySDO>> activityTypeMap = new HashMap<>();
        ForecastOrderActivityResponse forecastOrderActivityResponse = new ForecastOrderActivityResponse();
        if (CollectionUtils.isNotEmpty(activitySDOS)) {
            Nullable.stream(activitySDOS).forEach(o->o.setUnitPrice(forecastOrderLineSDO.getUnitPrice()));
            activityTypeMap = Nullable.stream(activitySDOS).collect(Collectors.groupingBy(ActivitySDO::getActivityType));
        }
        List<ActivitySDO> activityChooseList = new ArrayList<>();
        if (MapUtils.isNotEmpty(activityTypeMap)) {
            activityChooseList = activityTypeMap.get("ZS42");
            if (CollectionUtils.isEmpty(activityChooseList)) {
                activityChooseList = activityTypeMap.get("ZS02");
            }
            forecastOrderActivityResponse.setActivityChooseList(activityChooseList);
            forecastOrderActivityResponse.setActivitySpecialChooseList(activityTypeMap.get("ZS03"));

            //设置预报订单id
            forecastOrderActivityResponse.setId(forecastOrderSDO.getId());
            //设置预报业务id
            forecastOrderActivityResponse.setOrderBizId(forecastOrderSDO.getOrderBizId());
            //设置选中的活动id
            List<ChooseActivityResponse> activitySelectList = new ArrayList<>();

            if (MapUtils.isNotEmpty(activityTypeMap)) {
                List<ActivitySDO> activityZS42SDOS = activityTypeMap.get("ZS42");
                if (CollectionUtils.isNotEmpty(activityZS42SDOS)) {
                    ChooseActivityResponse chooseActivityZs42Response = selectFirstActivityResponse(activityZS42SDOS);
                    activitySelectList.add(chooseActivityZs42Response);
                }
                //特价默认选择一个
                List<ActivitySDO> activitySpecialSDOS = activityTypeMap.get("ZS03");
                if (CollectionUtils.isNotEmpty(activitySpecialSDOS)) {
                    ChooseActivityResponse chooseActivityZs03Response = selectFirstActivityResponse(activitySpecialSDOS);
                    activitySelectList.add(chooseActivityZs03Response);
                }
                //搭赠默认选择一个
                List<ActivitySDO> activityChooseSDOS = activityTypeMap.get("ZS02");
                if (CollectionUtils.isNotEmpty(activityChooseSDOS)) {
                    ChooseActivityResponse chooseActivitySpecialResponse = selectFirstActivityResponse(activityChooseSDOS);
                    activitySelectList.add(chooseActivitySpecialResponse);
                }
            }
            forecastOrderActivityResponse.setSelectedActivities(activitySelectList);
            //货品id
            forecastOrderActivityResponse.setScItemId(forecastOrderLineSDO.getScItemId());
            //货品名称
            forecastOrderActivityResponse.setScItemName(forecastOrderLineSDO.getScItemTitle());
            //行号
            forecastOrderActivityResponse.setLineNo(String.valueOf(forecastOrderLineSDO.getSerialNumber()));
            //客户名称
            forecastOrderActivityResponse.setCustomerName(forecastOrderSDO.getCustomerName());
            //预报子单id
            forecastOrderActivityResponse.setForecastOrderLineId(String.valueOf(forecastOrderLineSDO.getId()));
            //predictReplyAmount 预计回复数量
            forecastOrderActivityResponse.setPredictReplyAmount(forecastOrderLineSDO.getPredictReplyAmount());

        }
        return forecastOrderActivityResponse;
    }

    @NotNull
    private ChooseActivityResponse selectFirstActivityResponse(List<ActivitySDO> activitySDOList) {
        ChooseActivityResponse chooseActivityZs42Response = new ChooseActivityResponse();

        if (CollectionUtils.isNotEmpty(activitySDOList)) {
            chooseActivityZs42Response.setActivityCode(activitySDOList.get(0).getActivityCode());
            chooseActivityZs42Response.setActivityType(activitySDOList.get(0).getActivityType());
            chooseActivityZs42Response.setGiftList(activitySDOList.get(0).getChooseGiftList());

            //普通搭赠多赠品活动的赠品总量处理
            if ("multiply_gift".equals(activitySDOList.get(0).getGiftActivityType())) {

                //平摊逻辑在这做处理
                List<GiftSDO> chooseGiftList = activitySDOList.get(0).getChooseGiftList();
                if (CollectionUtils.isNotEmpty(chooseGiftList)) {
                    List<GiftSDO> giftList = Lists.newArrayList();
                    chooseActivityZs42Response.setGiftList(giftList);
                    GiftSDO giftSDO = chooseGiftList.get(0);
                    if (Objects.nonNull(giftSDO) && Objects.nonNull(giftSDO.getGiftNumber()) && giftSDO.getGiftNumber() > 0) {
                        Integer giftCount = giftSDO.getGiftNumber();

                        int avg = giftCount / chooseGiftList.size();
                        int distributed = 0;

                        for (int i = 0; i < chooseGiftList.size() - 1; i++) {
                            GiftSDO fontGiftSDO = new GiftSDO();
                            BeanUtils.copyProperties(chooseGiftList.get(i), fontGiftSDO);
                            fontGiftSDO.setGiftNumber(avg);
                            giftList.add(fontGiftSDO);
                            distributed += avg;
                        }
                        //将剩余的分配给最后一个
                        int remainingUnDistribute = giftCount - distributed;
                        GiftSDO tailGiftSDO = new GiftSDO();
                        BeanUtils.copyProperties(chooseGiftList.get(chooseGiftList.size() - 1), tailGiftSDO);
                        tailGiftSDO.setGiftNumber(remainingUnDistribute);
                        giftList.add(tailGiftSDO);
                    }
                }
                List<GiftSDO> giftList = activitySDOList.get(0).getGiftList();
                if (CollectionUtils.isNotEmpty(giftList)) {
//                    chooseActivityZs42Response.setGiftTotalNumber(chooseGiftList.get(0).getGiftNumber());
                }
            }
        }
        return chooseActivityZs42Response;
    }

    private List<ActivitySDO> queryActivity(List<ForecastOrderLineSDO> forecastOrderLineSDOs, ForecastOrderSDO forecastOrderSDO) {
        List<ActivitySDO> activitySDOSAll = new ArrayList<>();
        ActivitySDO activityNewSDO = new ActivitySDO();
        for (ForecastOrderLineSDO forecastOrderLineSDO : forecastOrderLineSDOs) {
            List<ActivitySDO> activitySDOS = new ArrayList<>();
            Map<String, Object> features = forecastOrderLineSDO.getFeatures();
            //补货订单
            if (ForecastOrderTag.RESTOCK_FORECAST.getValue().equals(forecastOrderSDO.getOrderType())) {
                activityNewSDO.setActivityName(forecastOrderLineSDO.getActivityName());
                activityNewSDO.setActivityType("ZS42");
                //补货订单默认选中
                activityNewSDO.setIsSelect(true);
                activityNewSDO.setActivityCode(forecastOrderLineSDO.getActivityCode());
                activityNewSDO.setActivityTermid(features.get(ForecastConstants.ACTIVITY_NUMBER) == null ? "" : features.get(ForecastConstants.ACTIVITY_NUMBER).toString());
                activityNewSDO.setSubOrderId(String.valueOf(forecastOrderLineSDO.getId()));
                activityNewSDO.setActivitydeal(features.get(ForecastConstants.REPLENISHMENT_ACTIVITY_REMAINING_QUANTITY) == null ? 0 : Integer.parseInt(features.get(ForecastConstants.REPLENISHMENT_ACTIVITY_REMAINING_QUANTITY).toString()));
                activityNewSDO.setActivityfee(features.get(ForecastConstants.REPLENISHMENT_ACTIVITY_REMAINING_FEE) == null ? 0L : Long.parseLong(features.get(ForecastConstants.REPLENISHMENT_ACTIVITY_REMAINING_FEE).toString()));
                activityNewSDO.setIntensityOfActivityDesc(features.get(ForecastConstants.REPLENISHMENT_INTENSITY_OF_ACTIVITY_DESC) == null ? "" : String.valueOf(features.get(ForecastConstants.REPLENISHMENT_INTENSITY_OF_ACTIVITY_DESC)));
                activitySDOS.add(activityNewSDO);
            } else {
                activitySDOS = JSONObject.parseArray(JSON.toJSONString(features.get(ForecastConstants.ACTIVITY_DETAILS)), ActivitySDO.class);
                if (CollectionUtils.isNotEmpty(activitySDOS)) {
                    activitySDOS.stream().forEach(sdo -> sdo.setSubOrderId(String.valueOf(forecastOrderLineSDO.getId())));
                    //各类型默认选中第一个活动
                }
            }
            activitySDOSAll.addAll(Optional.ofNullable(activitySDOS).orElse(Lists.newArrayList()));
        }
        return activitySDOSAll;
    }

    @ProcessInvoker(printLog = true, remark = "查询活动明细")
    public ForecastOrderActivityResponse queryActivityDetails(QueryForecastActivityDetailsRequest queryForecastActivityDetailsRequest) {
        ForecastOrderActivityResponse forecastOrderActivityResponse = new ForecastOrderActivityResponse();
        ForecastOrderLineSDO forecastOrderLineSDO = forecastOrderLineQueryRepository.query(queryForecastActivityDetailsRequest.getId());
        ForecastOrderSDO forecastOrderSDO = forecastOrderQueryRepository.load(forecastOrderLineSDO.getMainOrderId());
        Map<String, Object> features = forecastOrderLineSDO.getFeatures();
        List<ActivitySDO> activitySDOS = new ArrayList<>();
        ActivitySDO activityNewSDO = new ActivitySDO();
        Map<String, List<ActivitySDO>> activityTypeMap = new HashMap<>();
        //补货订单
        if ("03".equals(forecastOrderSDO.getOrderType())) {
            activityNewSDO.setActivityName(forecastOrderLineSDO.getActivityName());
            activityNewSDO.setActivityType("ZS42");
            //补货订单默认选中
            activityNewSDO.setIsSelect(true);
            activityNewSDO.setActivityCode(forecastOrderLineSDO.getActivityCode());
            activityNewSDO.setActivityTermid(features.get(ForecastConstants.ACTIVITY_NUMBER) == null ? "" : String.valueOf(features.get(ForecastConstants.ACTIVITY_NUMBER)));
            activityNewSDO.setActivityfee(features.get(ForecastConstants.REPLENISHMENT_ACTIVITY_REMAINING_FEE) == null ? 0L : Long.parseLong(features.get(ForecastConstants.REPLENISHMENT_ACTIVITY_REMAINING_FEE).toString()));
            activityNewSDO.setActivitydeal(features.get(ForecastConstants.REPLENISHMENT_ACTIVITY_REMAINING_QUANTITY) == null ? 0 : Integer.parseInt(String.valueOf(features.get(ForecastConstants.REPLENISHMENT_ACTIVITY_REMAINING_QUANTITY))));
            activityNewSDO.setIntensityOfActivityDesc(features.get(ForecastConstants.REPLENISHMENT_INTENSITY_OF_ACTIVITY_DESC) == null ? "" : String.valueOf(features.get(ForecastConstants.REPLENISHMENT_INTENSITY_OF_ACTIVITY_DESC)));
            activitySDOS.add(activityNewSDO);
            forecastOrderActivityResponse.setActivityChooseList(activitySDOS);
        } else {
            activitySDOS = JSONObject.parseArray(JSON.toJSONString(features.get(ForecastConstants.ACTIVITY_DETAILS)), ActivitySDO.class);
            if (CollectionUtils.isNotEmpty(activitySDOS)) {
                activityTypeMap = activitySDOS.stream().collect(Collectors.groupingBy(ActivitySDO::getActivityType));
                forecastOrderActivityResponse.setActivityChooseList(activityTypeMap.get("ZS02"));
                forecastOrderActivityResponse.setActivitySpecialChooseList(activityTypeMap.get("ZS03"));
            } else {
                return new ForecastOrderActivityResponse();
            }
        }
        //设置预报订单id
        forecastOrderActivityResponse.setId(forecastOrderSDO.getId());
        //设置选中的活动id

        List<ChooseActivityResponse> activitySelectList = new ArrayList<>();
//        int activityTage = Integer.parseInt(String.valueOf(features.get(ForecastConstants.ACTIVITY_TAG)));
//        //activityTage==0 表示不需要移除
        if (CollectionUtils.isNotEmpty(activitySDOS)) {
            for (ActivitySDO activitySDO : activitySDOS) {
                //活动为已经选中的增加chooseList
                if (activitySDO.getIsSelect()) {
                    ChooseActivityResponse chooseActivityResponse = new ChooseActivityResponse();
                    chooseActivityResponse.setActivityCode(activitySDO.getActivityCode());
                    chooseActivityResponse.setActivityType(activitySDO.getActivityType());
                    chooseActivityResponse.setGiftList(activitySDO.getChooseGiftList());
                    activitySelectList.add(chooseActivityResponse);
                }
            }
        } else {
//            if (MapUtils.isNotEmpty(activityTypeMap)) {
//                //特价默认选择一个
//                List<ActivitySDO> activitySpecialSDOS = activityTypeMap.get("ZS03");
//                if(CollectionUtils.isNotEmpty(activitySpecialSDOS)) {
//                    ChooseActivityResponse chooseActivitySpecialResponse = selectFirstActivityResponse(activitySpecialSDOS);
//                    activitySelectList.add(chooseActivitySpecialResponse);
//                }
//                //搭赠默认选择一个
//                List<ActivitySDO> activityChooseSDOS = activityTypeMap.get("ZS02");
//                if(CollectionUtils.isNotEmpty(activitySpecialSDOS)) {
//                    ChooseActivityResponse chooseActivitySpecialResponse = selectFirstActivityResponse(activityChooseSDOS);
//                    activitySelectList.add(chooseActivitySpecialResponse);
//                }
//            }
        }

        forecastOrderActivityResponse.setSelectedActivities(activitySelectList);
        //设置预报业务id
        forecastOrderActivityResponse.setOrderBizId(forecastOrderSDO.getOrderBizId());
        //货品id
        forecastOrderActivityResponse.setScItemId(forecastOrderLineSDO.getScItemId());
        //货品名称
        forecastOrderActivityResponse.setScItemName(forecastOrderLineSDO.getScItemTitle());
        //行号
        forecastOrderActivityResponse.setLineNo(String.valueOf(forecastOrderLineSDO.getSerialNumber()));
        //客户名称
        forecastOrderActivityResponse.setCustomerName(forecastOrderSDO.getCustomerName());
        //predictReplyAmount 预计回复数量
        forecastOrderActivityResponse.setPredictReplyAmount(forecastOrderLineSDO.getPredictReplyAmount());
        //返回子订单id
        forecastOrderActivityResponse.setForecastOrderLineId(String.valueOf(forecastOrderLineSDO.getId()));

        return forecastOrderActivityResponse;
    }
    /**
     * 预报订单批量生成销售订单
     * @param orderSDOS
     */
//    public List<String> batchCreateOrder(List<OrderSDO> orderSDOS) {
//        List<String> orderIds = orderWriteRepositoryForeCast.batchCreateOrder(orderSDOS);
//        return orderIds;
//    }

    /**
     * 分析预报订单执行流程  ---最小起运量校验
     * 逻辑：
     * 1、对入参子单行进行起运量校验，校验通过加入后续处理队列
     * 2、只有能获取到起运量策略且小于起运量的情况下，才设置子单状态为起运量校验失败，不加入后续处理队列
     *
     * @param forecastOrderLineListToAnalyze
     * @return
     */
    @FunctionInvoker
    @Deprecated
    public List<ForecastOrderLineSDO> analysisShippingVolume(List<ForecastOrderLineSDO> forecastOrderLineListToAnalyze) {
        List<ForecastOrderLineSDO> forecastOrderLineListToNextStep = new ArrayList<>();
        //根据主订单进行分组
        Map<String, List<ForecastOrderLineSDO>> OrderLinesListGroupByMainOrder = Nullable.stream(forecastOrderLineListToAnalyze)
                .collect(Collectors.groupingBy(ForecastOrderLineSDO::getMainOrderId));

        for (Map.Entry<String, List<ForecastOrderLineSDO>> orderIdAndOrderLineEntry : OrderLinesListGroupByMainOrder.entrySet()) {
            String orderId = orderIdAndOrderLineEntry.getKey();
            List<ForecastOrderLineSDO> forecastOrderLineList = orderIdAndOrderLineEntry.getValue();
            //查询预报主单信息
            ForecastOrderSDO forecastOrderSDO = null;
//            List<ForecastOrderLineSDO> content = new ArrayList<>();
            try {
                //[读库]从库中加载主单信息*1
                forecastOrderSDO = forecastOrderQueryRepository.load(orderId);
                if (Objects.isNull(forecastOrderSDO)) {
                    log.warn("WARN:Load order:{} info failed in analyze shipping volume", orderId);
                    continue;
                }
                //将查到的主订单全部储存起来，
//                ForecastOrderLineSDO forecastOrderLineSDO = new ForecastOrderLineSDO();
//                forecastOrderLineSDO.setMainOrderId(orderId);
                forecastOrderMap.put(orderId, forecastOrderSDO);
                // content = forecastOrderLineQueryRepository.queyrLineDetailList(forecastOrderLineSDO);
//               content=entity.getValue();
            } catch (Exception e) {
                log.error("ERROR:查询主单数据异常", e);
                e.printStackTrace();
            }
            if (Objects.isNull(forecastOrderSDO)) {
                log.warn("WARN:get order failed:{}", orderId);
                continue;
            }

            // 查询最低起运量  根据主订单的先部门，再渠道 后组织  （都没有查到返回0）
            Integer inducement = queryInducement(forecastOrderSDO);
            //求此订单的预报总数量
            Integer forecastQuantity = Nullable.stream(forecastOrderLineList).mapToInt(it -> Integer.parseInt(it.getQuantity())).sum();
            log.info("Forecast quantity:{}", forecastQuantity);
            //获取分析使用的预报子订单信息
            String wareHouseCode = forecastOrderSDO.getWareHouseCode();
            //为什么设置仓库code？因为在下一步库存分解会使用，这谁想得到
            forecastOrderLineList.stream().forEach(it -> it.setWarehouseArea(wareHouseCode));

//            if (inducement != 0 && forecastQuantity < inducement) {
            //起运量为0或获取不到，或数量大于起运量，加入后续检查队列
            if (inducement == 0 || forecastQuantity >= inducement) {
                Nullable.stream(forecastOrderLineList).forEach(it -> {
                    forecastOrderLineListToNextStep.add(it);
                });

            } else {
                //如果预报订单数量不足启运量，不足起运量应该做什么？设置回复状态、设置回复原因、设置子单分析状态、设置预计回复量为0
                //将订单行过滤掉，不进行后续校验
                try {
                    log.info("analysisDeal Save analyze result,count:{} ", forecastOrderLineList.size());
                    setOrderLineListToInsufficentShipmentAndSaveToDB(forecastOrderLineList);
                    setDealLineStatusToAnalyzed(forecastOrderLineList);
                } catch (Exception e) {
//                    e.printStackTrace();
                    log.error("ERROR:Update forecast order line faild:" + e.getMessage(), e);
                }
            }
        }
        return forecastOrderLineListToNextStep;
    }

    /**
     * 设置处理队列状态,并入库
     *
     * @param forecastOrderLineList
     */
    private void setDealLineStatusToAnalyzed(List<ForecastOrderLineSDO> forecastOrderLineList) {
        //处理队列中状态进行改变
        List<String> lineIdList = forecastOrderLineList.stream().map(it -> String.valueOf(it.getId())).collect(Collectors.toList());
        DealLineSDO dealLineSDO = new DealLineSDO();
        dealLineSDO.setForecastOrderLineIdList(lineIdList);
        //[读库]获取dealline*1
        Result<List<DealLineQueue>> listResult = dealLineQueryRepository.queryProgressQueueResult(dealLineSDO);
        List<DealLineQueue> queueList = Nullable.stream(listResult.getResult()).map(it -> {
            it.setDealTime(new Date());
            it.setDealStatus(ForecastDealStatusSEO.ANALYSIS_ED.getStatus());
            return it;
        }).collect(Collectors.toList());
        //[写库]写入dealline*1
        dealLineWriteRepository.batchUpdate(queueList);
    }

    //查询起运量
    @FunctionInvoker
    public Integer queryInducement(ForecastOrderSDO forecastOrderSDO) {
        int number = 0;

        //查询起运量维度：组织、部门、渠道
        MoreConditionQueryRequest moreConditionQueryRequest = new MoreConditionQueryRequest();
        moreConditionQueryRequest.setStrategyType(StrategyTypeEnum.RISE_CARRY_AMOUNT.getCode());
        moreConditionQueryRequest.setOrganizationCode(forecastOrderSDO.getOrganizationCode());//forecastOrderSDO.getOrganizationCode()
        moreConditionQueryRequest.setDepartmentCode(forecastOrderSDO.getDepartmentCode());
        //moreConditionQueryRequest.setCustomerCode(forecastOrderSDO.getCustomerCode());
        moreConditionQueryRequest.setChannelCode(forecastOrderSDO.getChannelCode());//forecastOrderSDO.getChannelCode()
        log.info("查询起运量入参:" + JSONObject.toJSONString(moreConditionQueryRequest));
        try {
            Result<MoreConditionQueryResponse> moreConditionQueryResponseResult = strategyQueryService.moreConditionQuery(moreConditionQueryRequest);
            log.info("查询起运量出参:" + JSONObject.toJSONString(moreConditionQueryResponseResult));
            if (Objects.nonNull(moreConditionQueryResponseResult)
                    && Objects.nonNull(moreConditionQueryResponseResult.getResult())
                    && Objects.nonNull(moreConditionQueryResponseResult.getResult().getStrategySDO())
                    && CollectionUtils.isNotEmpty(moreConditionQueryResponseResult.getResult().getStrategySDO().getDimensionSDOList())) {
                //只获取第1条记录？
                String ruleValue = moreConditionQueryResponseResult.getResult().getStrategySDO().getDimensionSDOList().get(0).getRuleValue();
                if (StringUtils.isNotBlank(ruleValue)) {
                    number = Integer.parseInt(ruleValue);
                }
            }
        } catch (Exception e) {
            log.error("ERROR:查询起运量策略异常:" + e.getMessage(), e);
//            e.printStackTrace();
        }
        return number;
    }

    // todo 根据仓库、货品查询库存
    @FunctionInvoker
    private Double queryGoodsInventory(String house, AnalyzeProcessDO processDO, String scItemId) {
        Double availableQuantity = 0d;
        try {
            //支持es降级查询db
            GetEsDegradeValueResponse degradeValueResponse = esDegradeService.getEsDegradeValue();
            if(Objects.nonNull(degradeValueResponse) && degradeValueResponse.getResult()){
                ForecastAnalysisQueryInventoryRequest queryAvailableInventoryRequest = new ForecastAnalysisQueryInventoryRequest();
                queryAvailableInventoryRequest.setTypes(Lists.newArrayList(2, 3));
                queryAvailableInventoryRequest.setWarehouseAreaId(house);
                queryAvailableInventoryRequest.setGoodsId(scItemId);
                Result<List<ForecastAnalysisInventoryRequest>> queryInventoryResponse = crUnifiedInventoryService.forecastAnalysisQueryInventory(queryAvailableInventoryRequest);
                if (Objects.nonNull(queryInventoryResponse) && CollectionUtils.isNotEmpty(queryInventoryResponse.getResult())) {
                    availableQuantity = Double.parseDouble(queryInventoryResponse.getResult().get(0).getAvailableQuantity());
                }
            }else{
                QueryAvailableInventoryRequest queryAvailableInventoryRequest = new QueryAvailableInventoryRequest();
                queryAvailableInventoryRequest.setType("1");
                queryAvailableInventoryRequest.setWarehouseAreaId(house);
                queryAvailableInventoryRequest.setGoodsId(scItemId);
                availableQuantity = forecastOrderQueryRepository.queryInventory(queryAvailableInventoryRequest);
            }
            String lastMessage = "|【参数】库存类型:1" +
                    "|仓库id:" + house +
                    "|货品id:" + scItemId +
                    "|【结果】库存:" + availableQuantity;
            if (Objects.nonNull(processDO)) {
                processDO.setLastMessage(lastMessage);
            }
        } catch (Exception e) {
            if (Objects.nonNull(processDO)) {
                processDO.sysErrorLogInDistributeInventory("【参数】仓库id:" + house + "|货品id：" + scItemId + "|异常：库存查询失败", null, e);
                String lastError = "查询库存失败";
                processDO.setLastError(lastError);
                e.printStackTrace();
            }
        }

        return availableQuantity;
    }

    // todo 根据 组织  渠道  部门  查询订单分解库存是否属于有效期  有效  无效  无策略

    /**
     * 修改：
     * 有每日库存分解到结果， 才算有效， 如果没有分配到结果就是无效
     */
    @FunctionInvoker
    private ForecastOrderLineSDO validityCheck(ForecastOrderLineSDO forecastOrderLineSDO) {
        Boolean isEffective = false;
        try {
            //支持es降级查询db
            GetEsDegradeValueResponse degradeValueResponse = esDegradeService.getEsDegradeValue();
            ForecastOrderLineIndex forecastOrderLine = new ForecastOrderLineIndex();
            if(Objects.nonNull(degradeValueResponse) && degradeValueResponse.getResult()){
                ForecastOrderLineSDO sdo = forecastOrderLineQueryRepository.query(String.valueOf(forecastOrderLineSDO.getId()));
                forecastOrderLine.setSubFeatures(JSONObject.toJSONString(sdo.getFeatures()));
            }else{
                forecastOrderLine = forecastOrderLineQueryRepository.getForecastOrderLineIndex(forecastOrderLineSDO);
            }
            DailyInventoryLine inventoryAllotRep = forecastOrderLineQueryRepository.getDailyInventoryByCache(forecastOrderLine);
            if (inventoryAllotRep != null) {
//                BigDecimal dailyInventoryCnt = new BigDecimal(Optional.ofNullable(inventoryAllotRep.getDailyInventoryMap()).orElse(Maps.newHashMap()).get(forecastOrderLine.getSubScItemId()));
//                BigDecimal dailyInventoryDeductCnt = new BigDecimal(Optional.ofNullable(inventoryAllotRep.getDailyInventoryDeductMap()).orElse(Maps.newHashMap()).get(forecastOrderLine.getSubScItemId()));
//
//                if (dailyInventoryCnt.compareTo(BigDecimal.ZERO) >= 0 || dailyInventoryDeductCnt.compareTo(BigDecimal.ZERO) >= 0) {
//                    isEffective = true;
//                }
                isEffective = inventoryAllotRep.getValid();
            }
        } catch (Exception e) {
            log.error("ERROR:校验分配单有效性异常:" + e.getMessage(), e);
//            e.printStackTrace();
        } finally {
            if (MapUtils.isNotEmpty(forecastOrderLineSDO.getFeatures())) {
                forecastOrderLineSDO.getFeatures().put(ForecastConstants.CHECK_EFFECTIVENESS, String.valueOf(isEffective));
                //forecastOrderLineSDO.getFeatures().put(ForecastConstants.CHECK_EFFECTIVENESS, "true");
            } else {
                Boolean finalIsEffective = isEffective;
                forecastOrderLineSDO.setFeatures(new HashMap() {{
                    put(ForecastConstants.CHECK_EFFECTIVENESS, String.valueOf(finalIsEffective));
                }});
            }
        }


//        Boolean isEffective = false;
//        try {
//            StrategyPageQueryRequest strategyPageQueryRequest = new StrategyPageQueryRequest();
//            ForecastOrderSDO forecastOrderSDO = forecastOrderMap.get(forecastOrderLineSDO.getMainOrderId());
//            strategyPageQueryRequest.setOrganizationCode(forecastOrderSDO.getOrganizationCode());
//            //strategyPageQueryRequest.setStatus(1);//有效的策略
//            strategyPageQueryRequest.setType(StrategyTypeEnum.DAILY_INVENTORY.getCode());//查询类型分解库存有效性检查
//            strategyPageQueryRequest.setDir("gmt_create");
//            strategyPageQueryRequest.setAsc(false);
//            log.info("查询有效性策略入参:" + JSONObject.toJSONString(strategyPageQueryRequest));
//            Result<List<StrategyPageQueryResponse>> listResult = strategyQueryService.pageQuery(strategyPageQueryRequest);
//            //查到当前策略
//            log.info("查询有效性策略出参:" + JSONObject.toJSONString(listResult));
//            if (Objects.nonNull(listResult) && CollectionUtils.isNotEmpty(listResult.getResult())) {
//                StrategyQueryByIdRequest strategyQueryByIdRequest = new StrategyQueryByIdRequest();
//                strategyQueryByIdRequest.setId(listResult.getResult().get(0).getId());
//                log.info("查询有效性策略详情入参:" + JSONObject.toJSONString(strategyQueryByIdRequest));
//                Result<StrategyQueryByIdResponse> strategyDetailResult = strategyQueryService.queryById(strategyQueryByIdRequest);
//                log.info("查询有效性策略详情出参:" + JSONObject.toJSONString(strategyDetailResult));
//                if (Objects.nonNull(strategyDetailResult) && Objects.nonNull(strategyDetailResult.getResult())
//                        && Objects.nonNull(strategyDetailResult.getResult().getStrategySDO())
//                        && CollectionUtils.isNotEmpty(strategyDetailResult.getResult().getStrategySDO().getDimensionSDOList())
//                ) {
//                    StrategySDO strategySDO = strategyDetailResult.getResult().getStrategySDO();
//                    DimensionSDO dimensionSDO =strategySDO .getDimensionSDOList().get(0);
//                    //此处日期格式可能存在问题
//                    isEffective = (Objects.nonNull(strategySDO.getValidBeginTime())&&Objects.nonNull(strategySDO.getValidEndTime()))&&(strategySDO.getValidBeginTime().compareTo(new Date()) <= 0) && (strategySDO.getValidEndTime().compareTo(new Date()) >= 0);
//                }
//            }
//        } catch (Exception e) {
//            log.error("ERROR:查询有效性策略异常:" + e.getMessage(), e);
//            e.printStackTrace();
//        } finally {
//            if (MapUtils.isNotEmpty(forecastOrderLineSDO.getFeatures())) {
//                forecastOrderLineSDO.getFeatures().put(ForecastConstants.CHECK_EFFECTIVENESS, String.valueOf(isEffective));
//                //forecastOrderLineSDO.getFeatures().put(ForecastConstants.CHECK_EFFECTIVENESS, "true");
//            } else {
//                Boolean finalIsEffective = isEffective;
//                forecastOrderLineSDO.setFeatures(new HashMap() {{
//                    put(ForecastConstants.CHECK_EFFECTIVENESS, String.valueOf(finalIsEffective));
//                }});
//            }
//        }
        //有效  无效
        return forecastOrderLineSDO;
    }

    //    查询出未走量的订单，并且设置未走量  找张苏徽接口
    private List<ForecastOrderLineSDO> walkQuantity(List<ForecastOrderLineSDO> forecastOrderLineSDOS) {
        for (ForecastOrderLineSDO forecastOrderLineSDO : forecastOrderLineSDOS) {
            forecastOrderLineSDO.getFeatures().put("是否为走量", "");//true false
            forecastOrderLineSDO.getFeatures().put("未走量值", "");
        }
        return forecastOrderLineSDOS;
    }

    //查询优先客户
    @FunctionInvoker
    public List<ForecastOrderLineSDO> preferredCustomers(List<ForecastOrderLineSDO> forecastOrderLineSDOS, AnalyzeProcessDO processDO) {
        //规避重复查询，初始value设置null代表未进行查询
        Map</* orderId */String, /* priorityFlag */ Boolean> orderLine2OrderIdMap = Maps.newHashMap();
        Map</* orderId */ String, List<StrategyPageQueryResponse>> tmpMap = processDO.getOrderStrategyPageQueryResponseMap();
        for (ForecastOrderLineSDO line : forecastOrderLineSDOS) {
            orderLine2OrderIdMap.put(line.getMainOrderId(), null);
        }

        Map<String, ForecastOrderSDO> forecastOrderMap = processDO.getForecastOrderMap();
        for (ForecastOrderLineSDO forecastOrderLineSDO : forecastOrderLineSDOS) {
            Boolean priorityFlag = orderLine2OrderIdMap.get(forecastOrderLineSDO.getMainOrderId());
            try {
                if (priorityFlag == null) {

                    //未查询则执行查询链路，默认设置为false，follow原代码逻辑
                    priorityFlag = false;

                    StrategyPageQueryRequest strategyPageQueryRequest = new StrategyPageQueryRequest();
                    ForecastOrderSDO forecastOrderSDO = forecastOrderMap.get(forecastOrderLineSDO.getMainOrderId());
                    strategyPageQueryRequest.setOrganizationCode(forecastOrderSDO.getOrganizationCode());
                    strategyPageQueryRequest.setStatus(1);//有效的策略
                    strategyPageQueryRequest.setType(StrategyTypeEnum.First_REPLY.getCode());//查询优先回复策略
                    log.info("查询优先性策略入参" + JSONObject.toJSONString(strategyPageQueryRequest));
                    Result<List<StrategyPageQueryResponse>> listResult = strategyQueryService.pageQuery(strategyPageQueryRequest);
                    log.info("查询优先策略出参" + JSONObject.toJSONString(listResult));
                    //查到当前策略
                    if (Objects.nonNull(listResult) && CollectionUtils.isNotEmpty(listResult.getResult())) {
                        StrategyQueryByIdRequest strategyQueryByIdRequest = new StrategyQueryByIdRequest();
                        strategyQueryByIdRequest.setId(listResult.getResult().get(0).getId());
                        strategyQueryByIdRequest.setCode(DimensionTypeEnum.CUSTOMER.getCode());
                        log.info("查询优先策略详情入参" + JSONObject.toJSONString(strategyQueryByIdRequest));
                        Result<StrategyQueryByIdResponse> strategyQueryByIdResponseResult = strategyQueryService.queryById(strategyQueryByIdRequest);
                        List<DimensionSDO> dimensionSDOList = strategyQueryByIdResponseResult.getResult().getStrategySDO().getDimensionSDOList();
                        log.info("查询优先策略详情出参" + JSONObject.toJSONString(dimensionSDOList));
                        priorityFlag = Nullable.stream(dimensionSDOList).anyMatch(it -> forecastOrderSDO.getCustomerCode().equalsIgnoreCase(it.getValue()));
                        // 暂存一下，减少查询
                        if (tmpMap == null) {
                            tmpMap = Maps.newHashMap();
                        }
                        tmpMap.put(forecastOrderLineSDO.getMainOrderId(), listResult.getResult());
                    }
                }
            } catch (Exception e) {
                log.error("ERROR:查询优先客户策略异常" + e.getMessage(), e);

                //TODO 这里是不是应该阻塞？
//                e.printStackTrace();
            } finally {
                orderLine2OrderIdMap.put(forecastOrderLineSDO.getMainOrderId(), priorityFlag);

                if (MapUtils.isNotEmpty(forecastOrderLineSDO.getFeatures())) {
                    // TBD ? 处理异常后，优先客户检查是错误的
                    forecastOrderLineSDO.getFeatures().put(ForecastConstants.CHECK_CUSTOMER, String.valueOf(priorityFlag));//true false  是否优先客户
                } else {
                    Boolean finalPriorityFlag = priorityFlag;
                    forecastOrderLineSDO.setFeatures(new HashMap() {{
                        put(ForecastConstants.CHECK_CUSTOMER, String.valueOf(finalPriorityFlag));
                    }});
                }
            }
        }
        return forecastOrderLineSDOS;
    }
    //   查询优先客户

    /**
     * 性能优化后的优先客户查询
     *
     * @param forecastOrderLineSDOS
     * @param processDO
     * @return
     */
    @FunctionInvoker
    public List<ForecastOrderLineSDO> newPreferredCustomers(List<ForecastOrderLineSDO> forecastOrderLineSDOS, AnalyzeProcessDO processDO) {
        Map<String, ForecastOrderSDO> forecastOrderMap = processDO.getForecastOrderMap();
        Map<String, Boolean> orgAndFalgMap = new HashMap<>();
        Map<String, String> orgAndStrategyIdMap = new HashMap<>();

        for (ForecastOrderLineSDO forecastOrderLineSDO : forecastOrderLineSDOS) {
            Boolean priorityFlag = false;
            StrategyPageQueryRequest strategyPageQueryRequest = new StrategyPageQueryRequest();
            ForecastOrderSDO forecastOrderSDO = forecastOrderMap.get(forecastOrderLineSDO.getMainOrderId());
            String orgCode = forecastOrderSDO.getOrganizationCode();
            try {
                if (Objects.nonNull(orgAndFalgMap.get(orgCode))) {
//                    priorityFlag = orgAndFalgMap.get(orgCode);
//                    if (Objects.isNull(priorityFlag)) {
//                        priorityFlag = Boolean.FALSE;
//                    }
//                    if (MapUtils.isNotEmpty(forecastOrderLineSDO.getFeatures())) {
//                        // TBD ? 处理异常后，优先客户检查是错误的
//                        forecastOrderLineSDO.getFeatures().put(ForecastConstants.CHECK_CUSTOMER, String.valueOf(priorityFlag));//true false  是否优先客户
//                    } else {
//                        Boolean finalPriorityFlag = priorityFlag;
//                        forecastOrderLineSDO.setFeatures(new HashMap() {{
//                            put(ForecastConstants.CHECK_CUSTOMER, String.valueOf(finalPriorityFlag));
//                        }});
//                    }
                    continue;
                }
                String strategyId = "";
                if (StringUtils.isNotEmpty(orgAndStrategyIdMap.get(orgCode))) {
                    strategyId = orgAndStrategyIdMap.get(orgCode);
                } else {
                    strategyPageQueryRequest.setOrganizationCode(forecastOrderSDO.getOrganizationCode());
                    strategyPageQueryRequest.setStatus(1);//有效的策略
                    strategyPageQueryRequest.setType(StrategyTypeEnum.First_REPLY.getCode());//查询优先回复策略
                    log.info("查询优先性策略入参" + JSONObject.toJSONString(strategyPageQueryRequest));
                    Result<List<StrategyPageQueryResponse>> listResult = strategyQueryService.pageQuery(strategyPageQueryRequest);
                    log.info("查询优先策略出参" + JSONObject.toJSONString(listResult));
                    if (Objects.nonNull(listResult) &&
                            CollectionUtils.isNotEmpty(listResult.getResult()) &&
                            Objects.nonNull(listResult.getResult().get(0))) {
                        strategyId = listResult.getResult().get(0).getId();
                    }
                }

                //查到当前策略
                StrategyQueryByIdRequest strategyQueryByIdRequest = new StrategyQueryByIdRequest();
                strategyQueryByIdRequest.setId(strategyId);
                strategyQueryByIdRequest.setCode(DimensionTypeEnum.CUSTOMER.getCode());
                log.info("查询优先策略详情入参" + JSONObject.toJSONString(strategyQueryByIdRequest));
                Result<StrategyQueryByIdResponse> strategyQueryByIdResponseResult = strategyQueryService.queryById(strategyQueryByIdRequest);
                List<DimensionSDO> dimensionSDOList = strategyQueryByIdResponseResult.getResult().getStrategySDO().getDimensionSDOList();
                log.info("查询优先策略详情出参" + JSONObject.toJSONString(dimensionSDOList));
                priorityFlag = Nullable.stream(dimensionSDOList).anyMatch(it -> {
                    return forecastOrderSDO.getCustomerCode().equalsIgnoreCase(it.getValue());
                });
                orgAndFalgMap.put(orgCode, priorityFlag);
            } catch (Exception e) {
                log.error("ERROR:查询优先客户策略异常" + e.getMessage(), e);
//                e.printStackTrace();
            } finally {
                priorityFlag = orgAndFalgMap.get(forecastOrderSDO.getOrganizationCode());
                if (Objects.isNull(priorityFlag)) {
                    priorityFlag = Boolean.FALSE;
                }
                if (MapUtils.isNotEmpty(forecastOrderLineSDO.getFeatures())) {
                    // TBD ? 处理异常后，优先客户检查是错误的
                    forecastOrderLineSDO.getFeatures().put(ForecastConstants.CHECK_CUSTOMER, String.valueOf(priorityFlag));//true false  是否优先客户
                } else {
                    Boolean finalPriorityFlag = priorityFlag;
                    forecastOrderLineSDO.setFeatures(new HashMap() {{
                        put(ForecastConstants.CHECK_CUSTOMER, String.valueOf(finalPriorityFlag));
                    }});
                }
            }
        }
        return forecastOrderLineSDOS;
    }

    //   todo  查询优先渠道
    @FunctionInvoker
    private List<ForecastOrderLineSDO> preferredChannel(List<ForecastOrderLineSDO> forecastOrderLineSDOS, AnalyzeProcessDO processDO) {
        Map<String, ForecastOrderSDO> forecastOrderMap = processDO.getForecastOrderMap();
        for (ForecastOrderLineSDO forecastOrderLineSDO : forecastOrderLineSDOS) {
            try {
                //将优先渠道初始状态设置为false
                forecastOrderLineSDO.getFeatures().put(ForecastConstants.CHECK_CHANNEL, "false");
                StrategyPageQueryRequest strategyPageQueryRequest = new StrategyPageQueryRequest();
                ForecastOrderSDO forecastOrderSDO = forecastOrderMap.get(forecastOrderLineSDO.getMainOrderId());
                strategyPageQueryRequest.setOrganizationCode(forecastOrderSDO.getOrganizationCode());
                strategyPageQueryRequest.setStatus(1);//有效的策略
                strategyPageQueryRequest.setType(StrategyTypeEnum.First_REPLY.getCode());//查询优先回复策略
                log.info("查询优先渠道入参" + JSONObject.toJSONString(strategyPageQueryRequest));
                Result<List<StrategyPageQueryResponse>> listResult = strategyQueryService.pageQuery(strategyPageQueryRequest);
                log.info("查询优先渠道出参" + JSONObject.toJSONString(listResult));
                //查到当前策略

                if (Objects.nonNull(listResult.getResult()) && listResult.getResult().size() > 0) {
                    List<String> collect = new ArrayList<>();
                    //查询渠道策略详情
                    StrategyQueryByIdRequest channelRequest = new StrategyQueryByIdRequest();
                    channelRequest.setId(listResult.getResult().get(0).getId());
                    channelRequest.setStatus("1");
                    channelRequest.setCode(DimensionTypeEnum.CHANNEL.getCode());
                    log.info("查询优先渠道详情入参" + JSONObject.toJSONString(channelRequest));
                    Result<StrategyQueryByIdResponse> channelResponseResult = strategyQueryService.queryById(channelRequest);
                    log.info("查询优先渠道详情入参" + JSONObject.toJSONString(channelResponseResult));
                    if (Objects.nonNull(channelResponseResult.getResult())
                            && Objects.nonNull(channelResponseResult.getResult().getStrategySDO())) {
                        List<DimensionSDO> channelSDOList = channelResponseResult.getResult().getStrategySDO().getDimensionSDOList();
                        if (CollectionUtils.isEmpty(channelSDOList)) {
                            log.warn("WARN:dimention sdo is null, in query preffered channel list:{}", forecastOrderLineSDO.getId());
                            continue;
                        }
                        for (DimensionSDO dimensionSDO : channelSDOList) {
                            //将对应的渠道和优先级赋值给预报订单features
                            if (Objects.equals(dimensionSDO.getValue(), forecastOrderSDO.getChannelCode())) {
                                forecastOrderLineSDO.getFeatures().put(ForecastConstants.CHECK_CHANNEL, "true");
                                forecastOrderLineSDO.getFeatures().put(ForecastConstants.CHECK_CHANNEL_PRIORITY, dimensionSDO.getPriority().toString());//排序
                                break;
                            }
                        }
                    }
                }
            } catch (Exception e) {
                log.error("ERROR:查询渠道策略异常" + e.getMessage(), e);
//                e.printStackTrace();
            }
        }
        return forecastOrderLineSDOS;
    }

    //   todo  查询优先部门
    //preferredDepartment
    @FunctionInvoker
    private List<ForecastOrderLineSDO> preferredDepartment(List<ForecastOrderLineSDO> forecastOrderLineSDOS, AnalyzeProcessDO processDO) {
        //规避重复查询，初始value设置null代表未进行查询
        Map</* orderId */String, /* priority */ Integer> orderLine2OrderIdMap = Maps.newHashMap();
        for (ForecastOrderLineSDO line : forecastOrderLineSDOS) {
            orderLine2OrderIdMap.put(line.getMainOrderId(), null);
        }

        //备注，这里因为部门查询在客户查询之后，因此这里不用做额外赋值，仅查询
        Map</* orderId */ String, List<StrategyPageQueryResponse>> cacheMap = processDO.getOrderStrategyPageQueryResponseMap();

        for (ForecastOrderLineSDO forecastOrderLineSDO : forecastOrderLineSDOS) {
            try {
                //将初始优先部门状态设置为false
                forecastOrderLineSDO.getFeatures().put(ForecastConstants.CHECK_DEPARTMENT, "false");

                //如果已缓存，则取缓存值
                Integer cachePriority = orderLine2OrderIdMap.get(forecastOrderLineSDO.getMainOrderId());
                if (cachePriority != null) {
                    forecastOrderLineSDO.getFeatures().put(ForecastConstants.CHECK_DEPARTMENT, "true");
                    forecastOrderLineSDO.getFeatures().put(ForecastConstants.CHECK_DEPARTMENT_PRIORITY, String.valueOf(cachePriority));//排序
                    continue;
                }

                StrategyPageQueryRequest strategyPageQueryRequest = new StrategyPageQueryRequest();
                ForecastOrderSDO forecastOrderSDO = processDO.getForecastOrderMap().get(forecastOrderLineSDO.getMainOrderId());
                strategyPageQueryRequest.setOrganizationCode(forecastOrderSDO.getOrganizationCode());
                strategyPageQueryRequest.setStatus(1);//有效的策略
                strategyPageQueryRequest.setType(StrategyTypeEnum.First_REPLY.getCode());//查询优先回复策略

                //如果已经查询过则不再额外查询
                Result<List<StrategyPageQueryResponse>> listResult = Result.success(cacheMap.get(forecastOrderLineSDO.getMainOrderId()));
                if (listResult.getResult() == null) {
                    log.info("查询优先部门入参" + JSONObject.toJSONString(strategyPageQueryRequest));
                    listResult = strategyQueryService.pageQuery(strategyPageQueryRequest);
                    log.info("查询优先部门出参" + JSONObject.toJSONString(listResult));
                }

                //查到当前策略
                if (Objects.nonNull(listResult.getResult()) && listResult.getResult().size() > 0) {
                    //查询部门策略详情
                    StrategyQueryByIdRequest strategyQueryByIdRequest = new StrategyQueryByIdRequest();
                    strategyQueryByIdRequest.setId(listResult.getResult().get(0).getId());
                    strategyQueryByIdRequest.setStatus("1");
                    strategyQueryByIdRequest.setCode(DimensionTypeEnum.DEPARTMENT.getCode());
                    log.info("查询优先部门详情入参" + JSONObject.toJSONString(strategyQueryByIdRequest));
                    Result<StrategyQueryByIdResponse> strategyQueryByIdResponseResult = strategyQueryService.queryById(strategyQueryByIdRequest);
                    log.info("查询优先部门详情入参" + JSONObject.toJSONString(strategyQueryByIdResponseResult));
                    List<String> collect = new ArrayList<>();
                    if (Objects.nonNull(strategyQueryByIdResponseResult.getResult()) && Objects.nonNull(strategyQueryByIdResponseResult.getResult().getStrategySDO())) {
                        List<DimensionSDO> dimensionSDOList = strategyQueryByIdResponseResult.getResult().getStrategySDO().getDimensionSDOList();
                        if (CollectionUtils.isNotEmpty(dimensionSDOList)) {
                            for (DimensionSDO dimensionSDO : dimensionSDOList) {
                                //将对应的渠道和优先级赋值给预报订单features
                                if (Objects.equals(dimensionSDO.getValue(), forecastOrderSDO.getDepartmentCode())) {
                                    forecastOrderLineSDO.getFeatures().put(ForecastConstants.CHECK_DEPARTMENT, "true");
                                    forecastOrderLineSDO.getFeatures().put(ForecastConstants.CHECK_DEPARTMENT_PRIORITY, String.valueOf(dimensionSDO.getPriority()));//排序

                                    //暂存
                                    orderLine2OrderIdMap.put(forecastOrderLineSDO.getMainOrderId(), dimensionSDO.getPriority());
                                    break;
                                }
                            }
                        }

                    }
                }
            } catch (Exception e) {
                log.error("ERROR:查询部门策略异常" + e.getMessage(), e);

                //TODO 确认下异常处理策略？
            }
        }
        return forecastOrderLineSDOS;
    }

    /**
     * 分析预报订单执行流程  ---货品分配
     *
     * @param batchAnalysisOrdersRequest
     * @return
     */
    @FunctionInvoker
    @NotNull
    //500多行的巨无霸，需要优化
    @Deprecated
    public List<ForecastOrderLineSDO> analysisGoodsDistribution(List<ForecastOrderLineSDO> batchAnalysisOrdersRequest) {
        //处理结果集合
        List<ForecastOrderLineSDO> forecastOrderLineListToNextStep = new ArrayList<>();

        //先仓库分组 再根据货品// 预报订单行数据中保存仓库数据
        Map<String, List<ForecastOrderLineSDO>> orderLineGroupByHouse = Nullable.stream(batchAnalysisOrdersRequest)
                .collect(Collectors.groupingBy(ForecastOrderLineSDO::getWarehouseArea));
        //需要重新封装
        try {
            //根据仓库进行处理
            for (Map.Entry<String, List<ForecastOrderLineSDO>> entry : orderLineGroupByHouse.entrySet()) {
                //发货仓code
                String warehouseAreaCode = entry.getKey();
                //这些订单都是归属同一个主单、同一个仓库
                List<ForecastOrderLineSDO> forecastOrderLineSDOList = entry.getValue();
                //根据货品进行分组
                Map<String, List<ForecastOrderLineSDO>> collect = Nullable.stream(forecastOrderLineSDOList)
                        .collect(Collectors.groupingBy(ForecastOrderLineSDO::getScItemId));
                //货品进行分配
                for (Map.Entry<String, List<ForecastOrderLineSDO>> scItemEntry : collect.entrySet()) {
                    //同一仓库统一货品的订单
                    List<ForecastOrderLineSDO> forecastOrderLineByScItem = scItemEntry.getValue();
                    String unitConvertCount = forecastOrderLineByScItem.get(0).getUnitConvert();
                    int unitConvert = StringUtils.isNotBlank(unitConvertCount) ? Integer.parseInt(unitConvertCount) : 1;
                    // TODO 根据货品id和仓库查询库存,可用库存数量ScItemNumber
                    Integer number = queryGoodsInventory(warehouseAreaCode, null, scItemEntry.getKey()).intValue() * unitConvert;
                    //Integer number = 20;
                   /* Integer allCount = 0;
                    for (ForecastOrderLineSDO forecastOrderLineSDO : forecastOrderLineByScItem) {
                        allCount += converterNumber(forecastOrderLineSDO);
                    }*/
                    //库存不足所有订单全部返回
                    if (number <= 0) {
//                        setOrderLineListToInventoryShortage(forecastOrderLineByScItem);
                        setOrderLineListDealStatusWithReason(forecastOrderLineSDOList, AnalyzeReasonsSEO.INVENTORY_SHORTAGE, null);
                        forecastOrderLineListToNextStep.addAll(forecastOrderLineByScItem);
                    }
                    //如果库存满足总量 全部回复
                   /* else if (number >= allCount) {
                        setOrderLineListToTotallySatisfied(forecastOrderLineByScItem);
                        forecastOrderLineListToNextStep.addAll(forecastOrderLineByScItem);
                    } */
                    else {
                        distributeInventoryToOrderLines(forecastOrderLineListToNextStep, forecastOrderLineByScItem, number);
                    }
                }
            }
        } catch (Exception e) {
//            processDO.sysErrorLogInDistributeInventory("库存分解失败", null, e);
//            log.error("ERROR:Analyze goods distribution failed:{}", e.getMessage());
            e.printStackTrace();
        }

        return forecastOrderLineListToNextStep;
    }

    /**
     * 将有限库存根据策略分配到子单
     *
     * @param forecastOrderLineListToNextStep
     * @param forecastOrderLineByScItem
     * @param number
     */
    @Deprecated
    private void distributeInventoryToOrderLines(List<ForecastOrderLineSDO> forecastOrderLineListToNextStep,
                                                 List<ForecastOrderLineSDO> forecastOrderLineByScItem, Integer number) {
        //  根据 组织  渠道  部门  查询订单是否属于  有效  无效或无策略并且进行分组
        List<ForecastOrderLineSDO> tempOrderLineList;
        //处理步骤：1.检查预报子单是否匹配有效性策略，将子单分为有效组和无效组
        log.info("ForecastOrderLineAbility#Step1: check valid, count:{}", forecastOrderLineListToNextStep.size());
        //TODO：需要确认分析是否需要校验有效性？
        tempOrderLineList = forecastOrderLineByScItem.stream().map(it -> validityCheck(it)).collect(Collectors.toList());
        Map<Object, List<ForecastOrderLineSDO>> strategyMap = Nullable.stream(tempOrderLineList)
                .collect(Collectors.groupingBy(it -> it.getFeatures().get(ForecastConstants.CHECK_EFFECTIVENESS)));

        //如果在命中有效策略时，就已经库存不够分配，把无效分组置为库存不足
        boolean needToProcessInvalid = true;
        List<ForecastOrderLineSDO> trueOrderLineSDOList = strategyMap.get("true");
        //先分析有效订单
        if (CollectionUtils.isNotEmpty(trueOrderLineSDOList)) {
            //分解且有效的根据时间进行排序
            List<ForecastOrderLineSDO> efficientOrderLineList = Nullable.stream(trueOrderLineSDOList)
                    .sorted(Comparator.comparing(ForecastOrderLineSDO::getId).reversed()).collect(Collectors.toList());
            for (ForecastOrderLineSDO it : efficientOrderLineList) {
                //todo it.getFeatures().get(DISINTEGRATION_QTY).toString()) 有nop可能 需要和白兴雷确认有策略就一定有值
                //int quantity = (StringUtils.isNotEmpty(String.valueOf(it.getFeatures().get(DISINTEGRATION_QTY))) && Integer.parseInt(String.valueOf(it.getFeatures().get(DISINTEGRATION_QTY))) != 0) ? Integer.parseInt(String.valueOf(it.getFeatures().get(DISINTEGRATION_QTY))) : Integer.parseInt(it.getQuantity());
                //处理步骤：2.根据每日库存分配
                log.info("ForecastOrderLineAbility#Step2: daily inventory, count:{}", forecastOrderLineListToNextStep.size());
                int quantity = Objects.nonNull(it.getFeatures().get(DISINTEGRATION_QTY)) ? Integer.parseInt(String.valueOf(it.getFeatures().get(DISINTEGRATION_QTY))) : 0;
                //每日库存为0（没有分解每日库存）将子单放到无效分组里？
                if (quantity == 0) {
                    setOneOrderLineToInventoryShortage(0, it);
                    continue;
                }
                //剩余库存大于每日库存，直接将库存分配给子单，将子单状态设置为完全满足
                if (number >= converterNumber(it, String.valueOf(quantity))) {
                    // number : 仓库库存
                    // quantity ： 每日库存占用量
                    // it.getPredictReplyAmount  预计回复量
                    // it.getActualTotalReplyAmount() 累积回复量


                    number -= converterNumber(it, String.valueOf(quantity));

                    if (quantity < Integer.parseInt(it.getQuantity()) - Optional.ofNullable(it.getActualTotalReplyAmount()).orElse(0)) {
                        setOneOrderLineToInventoryShortage(converterNumber(it, String.valueOf(quantity)), it);
                    } else {
                        setOneOrderLineToTotallySatisfied(it, converterNumber(it, String.valueOf(quantity)));
                    }

                    forecastOrderLineListToNextStep.add(it);
                } else {
                    //否则将子单检查原因置为库存不足
                    setOneOrderLineToInventoryShortage(number, it);
                    number = 0;//货品总数量置0
                    forecastOrderLineListToNextStep.add(it);
                    //库存为空,将剩下的置为起运量不足放入集合 todo 在true的条件中 为什么要对false的内容进行操作？上面的代码已经处理出返回结果了 这里为啥还要进行相关数据的返回 应该挪到下面的else块
                    List<ForecastOrderLineSDO> FOrderLineSDOS = strategyMap.get("false");
                    if (!CollectionUtils.isEmpty(FOrderLineSDOS)) {
                        for (ForecastOrderLineSDO fOrderLineSDO : FOrderLineSDOS) {
                            setOneOrderLineToInventoryShortage(0, fOrderLineSDO);
                            forecastOrderLineListToNextStep.add(fOrderLineSDO);
                        }
                    }
                    //没有库存了，就不需要再处理了？
                    needToProcessInvalid = false;
                }
            }
        }
        //处理有效性检查失败子单、没有每日库存的子单，条件是每日库存分完后还有剩余？
        List<ForecastOrderLineSDO> falseOrderLineSDOList = strategyMap.get("false");
        if (needToProcessInvalid && CollectionUtils.isNotEmpty(falseOrderLineSDOList)) {
            //分解无效策略的订单，进行分析
            List<ForecastOrderLineSDO> invalidOrderLineList = falseOrderLineSDOList;

            List<ForecastOrderLineSDO> sortedInvalidOrderLineList = Nullable.stream(invalidOrderLineList)
                    .sorted(Comparator.comparing(ForecastOrderLineSDO::getId)).collect(Collectors.toList());

            List<ForecastOrderLineSDO> priorityInvalidOrderLineList = preferredCustomers(sortedInvalidOrderLineList, null);

            //根据优先客户进行分组
            Map<Object, List<ForecastOrderLineSDO>> groupPriorityList = Nullable.stream(priorityInvalidOrderLineList)
                    .collect(Collectors.groupingBy(it -> it.getFeatures().get(ForecastConstants.CHECK_CUSTOMER)));

            //优先客户子订单
            List<ForecastOrderLineSDO> priorityCustomerList = Nullable.stream(groupPriorityList.get("true"))
                    .sorted(Comparator.comparing(ForecastOrderLineSDO::getId)).collect(Collectors.toList());

            //是否需要分析普通客户，在优先客户已经分配完库存，无需再继续执行普通的客户订单
            //处理步骤：3.根据优先客户进行库存分配
            log.info("ForecastOrderLineAbility#Step3: customer priority, count:{}", forecastOrderLineListToNextStep.size());
            boolean needProcessNormalCustomer = true;
            for (ForecastOrderLineSDO it : priorityCustomerList) {
                Map features = MapUtils.isNotEmpty(it.getFeatures()) ? it.getFeatures() : new HashMap<String, Object>();
                //优先客户按照时间排序全量满足
                if (number >= Integer.parseInt(it.getQuantity())) {
                    setOneOrderLineToTotallySatisfied(it, converterNumber(it));
                    number -= converterNumber(it);//确认回复量，并减去总量
                    forecastOrderLineListToNextStep.add(it);
                } else {
                    setOneOrderLineToInventoryShortage(number, it);
                    //TODO：剩余库存不满足某个优先客户的提报量，为什么置为0？可能会满足其他客户的提报量
                    number = 0;//货品总数量置0
                    forecastOrderLineListToNextStep.add(it);
                    //货品为空,将剩下的置为起运量不足放入集合
                    //预处理，如果剩余库存已经不满足优先客户了，普通客户就不用分了，直接置为库存不足
                    List<ForecastOrderLineSDO> FOrderLineSDOS = groupPriorityList.get("false");
                    if (!CollectionUtils.isEmpty(FOrderLineSDOS)) {
                        for (ForecastOrderLineSDO fOrderLineSDO : FOrderLineSDOS) {
                            setOneOrderLineToInventoryShortage(0, fOrderLineSDO);
                            forecastOrderLineListToNextStep.add(fOrderLineSDO);
                        }
                    }
                    it.setFeatures(features);
                    needProcessNormalCustomer = false;
                }
            }
            //处理步骤4：根据普通客户进行分配
            log.info("ForecastOrderLineAbility#Step4: ordinary customer, count:{}", forecastOrderLineListToNextStep.size());
            List<ForecastOrderLineSDO> normalLineList = Nullable.stream(groupPriorityList.get("false"))
                    .sorted(Comparator.comparing(ForecastOrderLineSDO::getId).reversed()).collect(Collectors.toList());

            if (needProcessNormalCustomer && CollectionUtils.isNotEmpty(normalLineList)) {
                //处理步骤4.1：预报订单根据优先部门
                List<ForecastOrderLineSDO> departmentList = preferredDepartment(normalLineList, null);
                Map<Object, List<ForecastOrderLineSDO>> groupDepartmentMap = Nullable.stream(departmentList)
                        .collect(Collectors.groupingBy(it -> it.getFeatures().get(ForecastConstants.CHECK_DEPARTMENT)));
                //优先部门订单
                //是否需要再进入优先渠道，如果优先部门已经扣减没有库存了，不需要再分析优先渠道
                boolean intoChannel = true;
                List<ForecastOrderLineSDO> priorityDepartmentList = groupDepartmentMap.get("true");
                if (CollectionUtils.isNotEmpty(priorityDepartmentList) && number > 0) {
                    Integer count = 0;
                    for (ForecastOrderLineSDO forecastOrderLineSDO : priorityDepartmentList) {
                        count += converterNumber(forecastOrderLineSDO);
                    }
                    //库存大于需求全量回复
                    if (number >= count) {
                        for (ForecastOrderLineSDO it : priorityDepartmentList) {
                            setOneOrderLineToTotallySatisfied(it, converterNumber(it));
                            forecastOrderLineListToNextStep.add(it);
                        }
                        number -= count;//确认回复量，并减去总量
                    }
                    //库存小于需求回复
                    else {
                        List<ForecastOrderLineSDO> predictChannelList = new ArrayList<>();
                        Integer finalNumber = number;
                        //  todo  优先渠道客户部门分配比例查询   因为扣总比例为1 不会出现超库存总量为负数的情况
                        //处理步骤4.2：根据比例进行分配
                        List<ForecastOrderLineSDO> lastRatio = strategyCalculate.allocationProportionDepartment(priorityDepartmentList);
                        List<ForecastOrderLineSDO> sortedLastRatio = Nullable.stream(lastRatio)
                                .sorted(Comparator.comparing(item -> String.valueOf(item.getFeatures().get(ForecastConstants.CHECK_DEPARTMENT_PRIORITY)))).collect(Collectors.toList());
                        //Collections.reverse(sortedLastRatio);
                        for (ForecastOrderLineSDO it : sortedLastRatio) {
                            //  todo   如果预报量小于比例按预报量回复//
                            Object l = it.getFeatures().get(ForecastConstants.LAST_RATIO);//比例值
                            //去掉改成如果为null，请维护预报订单的，月度计划策略信息
                            if (Objects.isNull(l)) {
                                forecastOrderLineListToNextStep.addAll(sortedLastRatio);
                                break;
                            }
                            float scale = ((int) Float.parseFloat(String.valueOf(l)) * finalNumber) / 100;
                            Integer roundScale = (int) scale;//直接去尾部转换
                            if (converterNumber(it) > scale) {
                                /*String s = l.toString();*/
                                setOneOrderLineToInventoryShortage(converterNumber(it, roundScale.toString()), it);
                                number -= converterNumber(it, roundScale.toString());
                                //二次分配需要
                            } else {
                                setOneOrderLineToTotallySatisfied(it, converterNumber(it));
                                number -= converterNumber(it);
                                //二次分配需要
                            }
                            predictChannelList.add(it);
                        }

                        //按时间排序   todo  按照优先级分析 ForecastConstants.CHECK_DEPARTMENT_PRIORITY
                    /*List<ForecastOrderLineSDO> collect3 = Nullable.stream(predictChannelList)
                            .sorted(Comparator.comparing(item -> String.valueOf(item.getFeatures().get(ForecastConstants.CHECK_DEPARTMENT_PRIORITY)))).collect(Collectors.toList());*/
                        //Collections.reverse(collect3);

                        //按比例分配后没有余量
                        if (number <= 0) {
                            Nullable.stream(predictChannelList).forEach(it -> {
                                //订单直接放到处理结果集合
                                forecastOrderLineListToNextStep.add(it);
                            });
                        }
                        //处理步骤4.1.1：按比例分配后，将剩余量补给优先级高的子单
                        if (number > 0) {
                            for (ForecastOrderLineSDO it : sortedLastRatio) {
                                int diffNum = Integer.parseInt(it.getQuantity()) - it.getPredictReplyAmount();
                                //  todo 再次进行分配回复量   如果回复量小于预报量  且剩余量大于差量 则给订单补齐
                                if (diffNum > 0 && number >= diffNum) {
                                    Integer delta = Integer.parseInt(it.getQuantity()) - it.getPredictReplyAmount();
                                    setOneOrderLineToTotallySatisfied(it, Integer.parseInt(it.getQuantity()));
                                    forecastOrderLineListToNextStep.add(it);
                                    //剩余量计算
                                    number -= delta;
                                    continue;
                                }
                                //  todo 再次进行分配回复量   如果回复量小于预报量  且剩余量小于差量 则把剩下的都给订单
                                if (diffNum > 0) {
                                    setOneOrderLineToInventoryShortage(it.getPredictReplyAmount() + number, it);
                                    forecastOrderLineListToNextStep.add(it);
                                    //剩余量计算
                                    number = 0;//货品总数量置0
                                    continue;
                                }
                                //  todo 剩余量为0 或者 预报量大于等于回复量 直接放到集合返回
                                //TODO:为什么没有设置回复量？
                                it.setAnalysisReason(AnalyzeReasonsSEO.TOTALLY_SATISFIED.getName());
                                it.getFeatures().put(ForecastConstants.BULB_TYPE, AnalyzeReasonsSEO.TOTALLY_SATISFIED.getBulbType());
                                forecastOrderLineListToNextStep.add(it);
                            }
                            intoChannel = false;
                        }
                    }
                }
                //处理步骤4.2：优先渠道分配
                log.info("ForecastOrderLineAbility#Step4.2: channel priority, count:{}", forecastOrderLineListToNextStep.size());
                List<ForecastOrderLineSDO> prioriChannelList = Nullable.stream(groupDepartmentMap.get("false")).collect(Collectors.toList());
                if (intoChannel && CollectionUtils.isNotEmpty(prioriChannelList)) {

                    List<ForecastOrderLineSDO> channelLineList = preferredChannel(normalLineList, null);
                    Map<Object, List<ForecastOrderLineSDO>> groupChannelLineList = Nullable.stream(channelLineList)
                            .collect(Collectors.groupingBy(it -> it.getFeatures().get(ForecastConstants.CHECK_CHANNEL)));

                    //处理步骤4.2.1：优先渠道订单
                    List<ForecastOrderLineSDO> preferredChannel = groupChannelLineList.get("true");
                    if (CollectionUtils.isNotEmpty(preferredChannel) && number > 0) {
                        //优先渠道预报数量总数
                        Integer count = 0;
                        for (ForecastOrderLineSDO forecastOrderLineSDO : preferredChannel) {
                            count += converterNumber(forecastOrderLineSDO);
                        }
                        //库存大于需求全量回复
                        if (number >= count) {
                            for (ForecastOrderLineSDO it : preferredChannel) {
                                setOneOrderLineToTotallySatisfied(it, converterNumber(it));
                                forecastOrderLineListToNextStep.add(it);
                            }
                            number -= count;//确认回复量，并减去总量
                        } else {//库存小于需求回复
                            List<ForecastOrderLineSDO> predictChannelList = new ArrayList<>();
                            int finalNumber = number;
                            //  todo  优先渠道客户部门分配比例查询   因为扣总比例为1 不会出现超库存总量为负数的情况

                            //todo 这里应该是查询渠道的比例！！！！！！！！！！！！！！！！！！！！！！！！！！必须求证一下
                            List<ForecastOrderLineSDO> lastRatio = strategyCalculate.allocationProportionChannel(preferredChannel);
                            for (ForecastOrderLineSDO it : lastRatio) {
                                //it.getChannelCodes();//   todo   如果预报量小于比例按预报量回复//
                                Object l = it.getFeatures().get(ForecastConstants.LAST_RATIO);//比例值
                                //去掉改成如果为null，请维护预报订单的，月度计划策略信息
                                if (Objects.isNull(l)) {
                                    forecastOrderLineListToNextStep.addAll(lastRatio);
                                    break;
                                }
                                float v = ((int) Float.parseFloat(String.valueOf(l)) * finalNumber) / 100;//方法里面成了100
                                Integer k = (int) v;//直接去尾部转换
                                if (converterNumber(it) > v) {
                                    /*String s = l.toString();*/
                                    setOneOrderLineToInventoryShortage(converterNumber(it, k.toString()), it);
                                    number -= converterNumber(it, k.toString());
                                    //二次分配需要
                                } else {
                                    setOneOrderLineToTotallySatisfied(it, converterNumber(it));
                                    number -= converterNumber(it);
                                    //二次分配需要
                                }
                                predictChannelList.add(it);
                            }
                            //按渠道优先级排序  todo  ForecastConstants.CHECK_CHANNEL_PRIORITY
                            List<ForecastOrderLineSDO> orderChannelLineList = Nullable.stream(predictChannelList)
                                    .sorted(Comparator.comparing(item -> String.valueOf(item.getFeatures().get(ForecastConstants.CHECK_CHANNEL_PRIORITY)))).collect(Collectors.toList());
                            //Collections.reverse(orderChannelLineList);
                            //按比例分配后没有余量
                            if (number <= 0) {
                                forecastOrderLineListToNextStep.addAll(orderChannelLineList);
                            }
                            //按比例分配后还有余量
                            else {
                                for (ForecastOrderLineSDO forecastOrderLineSDO : orderChannelLineList) {
                                    //  todo 再次进行分配回复量   如果回复量小于预报量  且剩余量大于差量 则给订单补齐
                                    int diffNum = Integer.parseInt(forecastOrderLineSDO.getQuantity()) - forecastOrderLineSDO.getPredictReplyAmount();
                                    if (diffNum > 0) {
                                        if (number >= diffNum) {
                                            setOneOrderLineToTotallySatisfied(forecastOrderLineSDO, Integer.parseInt(forecastOrderLineSDO.getQuantity()));
                                            forecastOrderLineListToNextStep.add(forecastOrderLineSDO);
                                            //剩余量计算
                                            number -= diffNum;
                                        } else {
                                            //  todo 再次进行分配回复量   如果回复量小于预报量  且剩余量小于差量 则把剩下的都给订单
                                            setOneOrderLineToInventoryShortage(forecastOrderLineSDO.getPredictReplyAmount() + number, forecastOrderLineSDO);
                                            forecastOrderLineListToNextStep.add(forecastOrderLineSDO);
                                            //剩余量计算
                                            number = 0;//货品总数量置0
                                        }

                                    } else {
                                        //  todo 剩余量为0 或者 预报量大于等于回复量 直接放到集合返回
                                        //TODO:也没有设置回复量
                                        forecastOrderLineSDO.setAnalysisReason(AnalyzeReasonsSEO.TOTALLY_SATISFIED.getName());
                                        forecastOrderLineSDO.getFeatures().put(ForecastConstants.BULB_TYPE, AnalyzeReasonsSEO.TOTALLY_SATISFIED.getBulbType());
                                        forecastOrderLineListToNextStep.add(forecastOrderLineSDO);
                                    }
                                }
                            }

                        }
                    }
                    //处理步骤4.2.2：非优先渠道订单
                    log.info("ForecastOrderLineAbility#Step4.2: non channel priority check valid, count:{}", forecastOrderLineListToNextStep.size());
                    List<ForecastOrderLineSDO> unPreferredChannel = groupChannelLineList.get("false");
                    if (number > 0) {
                        List<ForecastOrderLineSDO> groupUnPreferredChannel = Nullable.stream(unPreferredChannel)
                                .sorted(Comparator.comparing(ForecastOrderLineSDO::getId)).collect(Collectors.toList());
                        for (ForecastOrderLineSDO it : groupUnPreferredChannel) {
                            if (number > converterNumber(it, it.getQuantity())) {//按照时间排序全量满足
                                setOneOrderLineToTotallySatisfied(it, Integer.parseInt(it.getQuantity()));
                                number -= converterNumber(it);//确认回复量，并减去总量
                                forecastOrderLineListToNextStep.add(it);
                                continue;
                            }
                            if (number < converterNumber(it) && number >= 0) {
                                setOneOrderLineToInventoryShortage(number, it);
                                number = 0;//货品总数量置0
                                forecastOrderLineListToNextStep.add(it);
                                continue;
                            }
                            if (number <= 0) {
                                setOneOrderLineToInventoryShortage(0, it);
                                forecastOrderLineListToNextStep.add(it);
                            }
                        }
                    } else {//库存不足将剩下订单原因置为预报订单库存不足
                        if (CollectionUtils.isNotEmpty(unPreferredChannel)) {
                            for (ForecastOrderLineSDO forecastOrderLineSDO : unPreferredChannel) {
                                setOneOrderLineToInventoryShortage(0, forecastOrderLineSDO);
                            }
                            forecastOrderLineListToNextStep.addAll(unPreferredChannel);
                        }
                    }
                }
            }

        }
    }


    /**
     * 设置子单为不足起运量：状态灯、分析原因、deal_status为分析完成、写库
     *
     * @param forecastOrderLineList
     */
    private void setOrderLineListToInsufficentShipmentAndSaveToDB(List<ForecastOrderLineSDO> forecastOrderLineList) {
        Nullable.stream(forecastOrderLineList).forEach(it -> {
            setOneOrderLineToInsufficientShipment(it);
            //给出回复原因直接打回
            // forecastOrderLineWriteRepository.analysis(it);// TODO 不继续参与下面的货品分配 优化成batch 批处理
        });
        //[写库]更新子单状态*1
        forecastOrderLineWriteRepository.batchUpdateForecastOrderLine(forecastOrderLineList);
    }

    /**
     * 设置子单为超过月度回复量：
     *
     * @param forecastOrderLineSDO
     */
    private void setOneOrderLineToOverMonthVolume(ForecastOrderLineSDO forecastOrderLineSDO) {
        forecastOrderLineSDO.setAnalysisReason(AnalyzeReasonsSEO.OVER_MONTH_VOLUME.getName());
        if (MapUtils.isNotEmpty(forecastOrderLineSDO.getFeatures())) {
            forecastOrderLineSDO.getFeatures().put(ForecastConstants.BULB_TYPE, AnalyzeReasonsSEO.OVER_MONTH_VOLUME.getBulbType());
        } else {
            forecastOrderLineSDO.setFeatures(new HashMap() {{
                put(ForecastConstants.BULB_TYPE, AnalyzeReasonsSEO.OVER_MONTH_VOLUME.getBulbType());
            }});
        }

        forecastOrderLineSDO.setAnalysisTime(new Date());
    }

    /**
     * 设置子订单检查状态为不足起运量，并设置处理状态为分析完成
     *
     * @param it
     */
    private void setOneOrderLineToInsufficientShipment(ForecastOrderLineSDO it) {
        it.setAnalysisReason(AnalyzeReasonsSEO.INSUFFICIENT_SHIPMENT.getName());
        if (MapUtils.isNotEmpty(it.getFeatures())) {
            it.getFeatures().put(ForecastConstants.BULB_TYPE, AnalyzeReasonsSEO.INSUFFICIENT_SHIPMENT.getBulbType());
        } else {
            it.setFeatures(new HashMap() {{
                put(ForecastConstants.BULB_TYPE, AnalyzeReasonsSEO.INSUFFICIENT_SHIPMENT.getBulbType());
            }});
        }
        it.setDealStatus(ForecastDealStatusSEO.ANALYSIS_ED.getStatus());
        it.setAnalysisTime(new Date());
        it.setPredictReplyAmount(0);
    }

    /**
     * 设置子订单检查状态为库存不足，并设置预计回复量
     *
     * @param number
     * @param it
     */
    @Deprecated
    private void setOneOrderLineToInventoryShortage(Integer number, ForecastOrderLineSDO it) {
        it.setAnalysisReason(AnalyzeReasonsSEO.INVENTORY_SHORTAGE.getName());
        if (MapUtils.isNotEmpty(it.getFeatures())) {
            it.getFeatures().put(ForecastConstants.BULB_TYPE, AnalyzeReasonsSEO.INVENTORY_SHORTAGE.getBulbType());
        } else {
            it.setFeatures(new HashMap() {{
                put(ForecastConstants.BULB_TYPE, AnalyzeReasonsSEO.INVENTORY_SHORTAGE.getBulbType());
            }});
        }
        it.setPredictReplyAmount(number);
        it.setAnalysisTime(new Date());
    }

    /**
     * 完全满足子单预报数量，并将状态置为完全满足，并设置预计回复量
     *
     * @param it
     * @param quantity
     */
    private void setOneOrderLineToTotallySatisfied(ForecastOrderLineSDO it, int quantity) {
        it.setAnalysisReason(AnalyzeReasonsSEO.TOTALLY_SATISFIED.getName());
        if (MapUtils.isNotEmpty(it.getFeatures())) {
            it.getFeatures().put(ForecastConstants.BULB_TYPE, AnalyzeReasonsSEO.TOTALLY_SATISFIED.getBulbType());
        } else {
            it.setFeatures(new HashMap() {{
                put(ForecastConstants.BULB_TYPE, AnalyzeReasonsSEO.TOTALLY_SATISFIED.getBulbType());
            }});
        }
        it.setPredictReplyAmount(quantity);
        it.setAnalysisTime(new Date());
    }

    /**
     * 库存分解后，再次检查是否满足起运量
     * 分析预报订单执行流程  ---起运量检查
     *
     * @param batchAnalysisOrdersRequest
     * @return
     */
    @FunctionInvoker
    @Deprecated
    public List<ForecastOrderLineSDO> analysisShippingVolumeSecond(List<ForecastOrderLineSDO> batchAnalysisOrdersRequest) {
        List<ForecastOrderLineSDO> forecastOrderLineSDOS = new ArrayList<>();
        //根据主订单进行分组
        Map<String, List<ForecastOrderLineSDO>> collect = Nullable.stream(batchAnalysisOrdersRequest)
                .collect(Collectors.groupingBy(ForecastOrderLineSDO::getMainOrderId));

        for (Map.Entry<String, List<ForecastOrderLineSDO>> entry : collect.entrySet()) {
            // TODO 查询最低起运量  根据主订单的组织
            Integer count = 0;
            Integer inducement = 0;
            //获取分析使用的预报子订单信息
            List<ForecastOrderLineSDO> forecastOrderLineSDOList = entry.getValue();

            try {
                ForecastOrderSDO load = forecastOrderQueryRepository.load(entry.getKey());
                inducement = queryInducement(load);
                count = Nullable.stream(forecastOrderLineSDOList).mapToInt(it -> {
                    int i = StringUtils.isNotBlank(it.getQuantity()) ? Integer.parseInt(it.getQuantity()) : 0;
                    return i;
                }).sum();
            } catch (Exception e) {
                log.error("ERROR:Analyze ship volume second failed:", e);
            }
            //如果已经回复数量不足启运量
            if (count < inducement) {
                Nullable.stream(forecastOrderLineSDOList).forEach(it -> {
                    setOneOrderLineToInsufficientShipment(it);
                    forecastOrderLineSDOS.add(it);
                });
            } else {
                Nullable.stream(forecastOrderLineSDOList).forEach(it -> {
                    forecastOrderLineSDOS.add(it);
                });
            }
        }
        return forecastOrderLineSDOS;
    }

    /**
     * 校验计划量
     *
     * @param batchAnalysisOrdersRequest
     * @return
     */
    @FunctionInvoker
    @Deprecated
    public List<ForecastOrderLineSDO> analysisPlanQuantity(List<ForecastOrderLineSDO> batchAnalysisOrdersRequest) {
        List<ForecastOrderLineSDO> forecastOrderLineSDOLists = new ArrayList<>();

        for (ForecastOrderLineSDO forecastOrderLineSDO : batchAnalysisOrdersRequest) {
            try {
                // todo 根据查询（可用量）
                Integer integer = strategyCalculate.pagePlanNumber(forecastOrderLineSDO);
                if (integer != -1 && integer < forecastOrderLineSDO.getPredictReplyAmount()) {
                    setOneOrderLineToOverMonthVolume(forecastOrderLineSDO);
                }
            } catch (Exception e) {
                log.error("ERROR:Analyze plan quntity failed:{}", e.getMessage());
            }
            forecastOrderLineSDOLists.add(forecastOrderLineSDO);
        }

        return forecastOrderLineSDOLists;
    }


    /**
     * 检查信贷
     *
     * @param batchAnalysisOrdersRequest
     * @return
     */
    @FunctionInvoker
    @Deprecated
    public List<ForecastOrderLineSDO> analysisCredit(List<ForecastOrderLineSDO> batchAnalysisOrdersRequest) {
        List<ForecastOrderLineSDO> forecastOrderLineSDOLists = new ArrayList<>();
        //根据主订单进行分组
        Map<String, List<ForecastOrderLineSDO>> collect = Nullable.stream(batchAnalysisOrdersRequest).
                collect(Collectors.groupingBy(ForecastOrderLineSDO::getMainOrderId));
        for (String mainOrderId : collect.keySet()) {
            //查询已经回复订单的总价
            List<ForecastOrderLineSDO> forecastOrderLineSDOList = collect.get(mainOrderId);

            ForecastOrderSDO forecastOrderInfo = forecastOrderQueryRepository.load(mainOrderId);
            long sum = Nullable.stream(forecastOrderLineSDOList).mapToLong(it -> {
                if (Objects.nonNull(it.getUnitPrice())) {
                    return it.getPredictReplyAmount() * it.getUnitPrice() / 100;
                }
                return 0;
            }).sum();

            // todo  查询信贷可用金额  1000  信贷检查不通过
            CreditQueryRequest creditQueryRequest = new CreditQueryRequest();
            creditQueryRequest.setCustomerCode(forecastOrderInfo.getCustomerCode());
            creditQueryRequest.setChannelCode(forecastOrderInfo.getChannelCode());
            creditQueryRequest.setOrganizationCode(forecastOrderInfo.getOrganizationCode());
            creditQueryRequest.setItemGroupCode(forecastOrderInfo.getProductGroupCode());
            Integer balance = null;
            try {
                Result<CreditInfoSDO> creditResponseResult = creditQueryService.queryCreditBalance(creditQueryRequest);

                if (Objects.nonNull(creditResponseResult) &&
                        Objects.nonNull(creditResponseResult.getResult()) &&
                        Objects.nonNull(creditResponseResult.getResult().getBalance())) {
                    balance = (int) (Float.parseFloat(creditResponseResult.getResult().getBalance()));
                }
            } catch (Exception e) {
                log.error("ERROR:Analyze credit failed", e);
//                e.printStackTrace();
            }
            //查不到信贷就不校验
            //TODO:如果查不到信用，是置为校验失败，还是通过？
            if (balance != null && sum > balance) {
                Nullable.stream(forecastOrderLineSDOList).forEach(it -> {
                    it.setAnalysisReason(AnalyzeReasonsSEO.INSUFFICIENT_ACCOUNTS.getName());
                    it.getFeatures().put(ForecastConstants.BULB_TYPE, AnalyzeReasonsSEO.INSUFFICIENT_ACCOUNTS.getBulbType());
                    forecastOrderLineSDOLists.add(it);
                });
            } else {
                Nullable.stream(forecastOrderLineSDOList).forEach(
                        it -> forecastOrderLineSDOLists.add(it)
                );
            }

        }

        return forecastOrderLineSDOLists;
    }

    /**
     * 剩余活动量不足拆单处理，后续生成不同的销售订单（主要特价促销活动）
     *
     * @param batchAnalysisOrdersRequest
     * @return
     */
    public List<ForecastOrderLineSDO> analysis6(List<ForecastOrderLineSDO> batchAnalysisOrdersRequest) {
        return null;
    }

    /**
     * 本次真实回复数据
     * 若存在箱规转换，则回复数量需要满足箱规整数倍
     * 参数  预报子订单   和 分解数量
     *
     * @param forecastOrderLineSDO
     * @return
     */
    @FunctionInvoker
    public Integer converterNumber(ForecastOrderLineSDO forecastOrderLineSDO, String disintegrationQty) {
        Integer newQuantity = NumberUtils.isNumber(disintegrationQty) ? Integer.parseInt(disintegrationQty) : 0;
        //是否考虑单位转化
        Boolean casesGauge = false;
        //查询当前的仓库的是否校验箱规策略
        Integer cartonValue = null;
        ForecastOrderSDO forecastOrderSDO = forecastOrderMap.get(forecastOrderLineSDO.getMainOrderId());
        DimensionSDO dimensionSDO = strategyCalculate.strategyType(forecastOrderSDO.getOrganizationCode());

        if (Objects.nonNull(dimensionSDO)) {
            CartonSepcsRequest cartonSepcsRequest = new CartonSepcsRequest();
            cartonSepcsRequest.setScItemId(forecastOrderLineSDO.getScItemId());
            Result<CartonSpecsSDO> cartonSpecsSDOResult = cartonSpecsQueryService.querySingle(cartonSepcsRequest);
            if (Objects.nonNull(cartonSpecsSDOResult) && Objects.nonNull(cartonSpecsSDOResult.getResult())) {
                cartonValue = cartonSpecsSDOResult.getResult().getSpecs();
            }

            casesGauge = dimensionSDO.getExecuteAction();
        }
        if (!casesGauge) {
            return newQuantity;
        }
        //没有累计回复数量
        if (Objects.isNull(forecastOrderLineSDO.getActualTotalReplyAmount())) {
            //没有转换关系
            if (!Objects.isNull(cartonValue)) {
                // int converter = Integer.parseInt(forecastOrderLineSDO.getUnitConvert());
                newQuantity = (int) Math.round((double) newQuantity / cartonValue) * cartonValue;
            }
            return newQuantity;
        } else {
            //预报数量小于等于累计回复数量
            if (forecastOrderLineSDO.getActualTotalReplyAmount() > Integer.parseInt(forecastOrderLineSDO.getQuantity())) {
                return 0;
            } else {
                //预报数量减去累计回复量大于分解量
                if ((Integer.parseInt(forecastOrderLineSDO.getQuantity()) - forecastOrderLineSDO.getActualTotalReplyAmount()) >= newQuantity) {
                    //不存在转换关系
                    if (!Objects.isNull(cartonValue)) {
                        // int converter = Integer.parseInt(forecastOrderLineSDO.getUnitConvert());
                        newQuantity = (int) Math.round((double) newQuantity / cartonValue) * cartonValue;
                    }
                    return newQuantity;
                } else {
                    //回复差量小于分解量且不存在转换关系
                    if (Objects.isNull(cartonValue)) {
                        //直接返回回复差量
                        return Integer.parseInt(forecastOrderLineSDO.getQuantity()) - forecastOrderLineSDO.getActualTotalReplyAmount();
                    } else {
                        //int converter = Integer.parseInt(forecastOrderLineSDO.getUnitConvert());
                        newQuantity = (int) Math.round(Integer.parseInt(forecastOrderLineSDO.getQuantity()) - (double) forecastOrderLineSDO.getActualTotalReplyAmount() / cartonValue) * cartonValue;
                        //返回转换后的回复差量
                        return newQuantity;
                    }
                }
            }
        }
    }

    /**
     * 本次真实回复数据
     * 为走量数据和已分解数据从这里求回复值
     *
     * @param forecastOrderLineSDO
     * @return
     */
    @FunctionInvoker
    public Integer converterNumber(ForecastOrderLineSDO forecastOrderLineSDO) {
        int quantity = Integer.parseInt(forecastOrderLineSDO.getQuantity());
        //是否考虑单位转化
        Boolean casesGauge = false;
        //查询当前的仓库的是否校验箱规策略
        Integer cartonValue = null;
        ForecastOrderSDO forecastOrderSDO = forecastOrderMap.get(forecastOrderLineSDO.getMainOrderId());
        DimensionSDO dimensionSDO = strategyCalculate.strategyType(forecastOrderSDO.getOrganizationCode());
        if (Objects.nonNull(dimensionSDO)) {
            CartonSepcsRequest cartonSepcsRequest = new CartonSepcsRequest();
            cartonSepcsRequest.setScItemId(forecastOrderLineSDO.getScItemId());
            Result<CartonSpecsSDO> cartonSpecsSDOResult = cartonSpecsQueryService.querySingle(cartonSepcsRequest);
            if (Objects.nonNull(cartonSpecsSDOResult) && Objects.nonNull(cartonSpecsSDOResult.getResult())) {
                cartonValue = cartonSpecsSDOResult.getResult().getSpecs();
            }

            casesGauge = dimensionSDO.getExecuteAction();
        }
        if (!casesGauge) {
            return quantity;
        }
        //没有累计回复数量
        if (Objects.isNull(forecastOrderLineSDO.getActualTotalReplyAmount())) {
            //没有转换关系
            if (Objects.nonNull(cartonValue)) {
                //int converter = Integer.parseInt(forecastOrderLineSDO.getUnitConvert());
                quantity = (int) Math.round((double) quantity / cartonValue) * cartonValue;
            }
            return quantity;
        } else {
            //累计回复数量大于等于预报数量
            if (forecastOrderLineSDO.getActualTotalReplyAmount() > quantity) {
                return 0;
            } else {
                if (Objects.isNull(cartonValue)) {
                    //直接返回回复差量
                    return quantity - forecastOrderLineSDO.getActualTotalReplyAmount();
                } else {
                    // int converter = Integer.parseInt(forecastOrderLineSDO.getUnitConvert());
                    quantity = (int) Math.round((double) quantity - (double) forecastOrderLineSDO.getActualTotalReplyAmount() / cartonValue) * cartonValue;
                    //返回转换后的回复差量
                    return quantity;
                }
            }
        }

    }

    /**
     * 订单检查初始化准备操作
     * 1、根据主订单进行分组
     * 2、从数据库中加载主单信息
     * 3、设置仓库code到待处理的子单信息中
     */
    public void prepareOrderLineListToAnalyze(AnalyzeProcessDO processDO) throws Exception {

        List<ForecastOrderLineSDO> orderLineListToAnalyze = processDO.getOrderLineListToAnalyze();
        Map<String, ForecastOrderSDO> forecastOrderMap = processDO.getForecastOrderMap();
        this.forecastOrderMap = forecastOrderMap;

        //根据主订单进行分组
        Map<String, List<ForecastOrderLineSDO>> OrderLinesListGroupByMainOrder = Nullable.stream(orderLineListToAnalyze)
                .collect(Collectors.groupingBy(ForecastOrderLineSDO::getMainOrderId));

        for (Map.Entry<String, List<ForecastOrderLineSDO>> orderIdAndOrderLineEntry : OrderLinesListGroupByMainOrder.entrySet()) {
            String orderId = orderIdAndOrderLineEntry.getKey();
            List<ForecastOrderLineSDO> forecastOrderLineList = orderIdAndOrderLineEntry.getValue();
            if (CollectionUtils.isEmpty(forecastOrderLineList)) {
                String lastMessage = "| 获取子单列表为空 | 主单order id:" + orderId;
                processDO.setLastMessage(lastMessage);
                processDO.bizAnalyzeLog("准备阶段", null, "子单列表为空");
                continue;
            }
            //查询预报主单信息
            ForecastOrderSDO forecastOrderSDO = forecastOrderMap.get(orderId);
            try {
                //减少非必要主单查询
                if (forecastOrderSDO == null) {
                    //[读库]从库中加载主单信息*1
                    forecastOrderSDO = forecastOrderQueryRepository.load(orderId);
                    if (Objects.isNull(forecastOrderSDO)) {
                        String lastMessage = "| 根据主单加载子单信息为空 | 主单order id:" + orderId;
                        processDO.setLastMessage(lastMessage);
                        processDO.bizAnalyzeLog("准备阶段", null, "加载主单信息为空");
                        throw new Exception(lastMessage);
                    }
                    forecastOrderMap.put(orderId, forecastOrderSDO);
                }
            } catch (Exception e) {
                String lastMessage = "| 查询主单数据异常 | order id:" + orderId;
                processDO.sysErrorLog("准备阶段", lastMessage, null, e);
                processDO.setLastMessage(lastMessage);
                //TODO FIX 这里异常应该直接阻断
                throw new Exception(lastMessage);
            }
            String wareHouseCode = forecastOrderSDO.getWareHouseCode();
            //初始化：设置仓库code,库存分解中使用，清除预计回复数量、分析原因、features字段
            for (ForecastOrderLineSDO orderLine : forecastOrderLineList) {
                orderLine.setWarehouseArea(wareHouseCode);
                orderLine.setPredictReplyAmount(0);
                orderLine.setAnalysisReason("");
                if (MapUtils.isEmpty(orderLine.getFeatures())) {
                    orderLine.setFeatures(new HashMap<>());
                }

                orderLine.getFeatures().put(ForecastConstants.DEPARTMENT_CODE, forecastOrderSDO.getDepartmentCode());
                orderLine.getFeatures().put(ForecastConstants.SALECHANNEL_CODE, forecastOrderSDO.getChannelCode());

            }
        }
    }

    /**
     * 重构后的起运量校验
     * 逻辑：
     * 1、获取所在主单校验起运量
     * 2、计算主单下需要分析的子单的总预报量
     * 3、能获取到起运量策略且小于起运量的情况下，才设置子单状态为起运量校验失败，分析完成，不再进行后续分析
     * 4、不能获取到起运量策略的，不做处理
     */
    @FunctionInvoker
    public void checkShippingVolume(AnalyzeProcessDO processDO) throws Exception {
        if (CollectionUtils.isEmpty(processDO.getOrderLineListToAnalyze())) {
            return;
        }
        List<ForecastOrderLineSDO> orderLineListToAnalyze = processDO.getOrderLineListToAnalyze();
        Map<String, ForecastOrderSDO> forecastOrderMap = processDO.getForecastOrderMap();
        //根据主订单进行分组
        Map<String, List<ForecastOrderLineSDO>> OrderLinesListGroupByMainOrder = Nullable.stream(orderLineListToAnalyze)
                .collect(Collectors.groupingBy(ForecastOrderLineSDO::getMainOrderId));

        Map</* orderId */ String, /* shipmentVolume */ Integer> orderShipmentVolumeMap = Maps.newHashMap();

        try {
            for (Map.Entry<String, List<ForecastOrderLineSDO>> orderIdAndOrderLineEntry : OrderLinesListGroupByMainOrder.entrySet()) {
                String orderId = orderIdAndOrderLineEntry.getKey();
                Integer mainOrderCacheShipmentVolume = orderShipmentVolumeMap.get(orderId);

                List<ForecastOrderLineSDO> forecastOrderLineList = orderIdAndOrderLineEntry.getValue();
                //查询预报主单信息
                ForecastOrderSDO forecastOrderSDO = forecastOrderMap.get(orderId);
                // 查询最低起运量，若之前未查询过，则重新发起一次查询
                Integer shipmentVolume = mainOrderCacheShipmentVolume;
                if (mainOrderCacheShipmentVolume == null) {
                    shipmentVolume = newGetShipmentVolume(forecastOrderSDO, processDO);
                    orderShipmentVolumeMap.put(orderId, shipmentVolume);
                }
                //求主单下选中的所有子单的预报总数量
                Integer forecastQuantity = Nullable.stream(forecastOrderLineList).mapToInt(it -> Integer.parseInt(it.getQuantity())).sum();
                //如果起运量不通过，将订单列表加入结果列表，后续不需要再检查
                if (shipmentVolume > 0 && forecastQuantity < shipmentVolume) {
//                    finishOrderLineListForInsufficentShipment(forecastOrderLineList);
                    finishOrderLineListForReason(forecastOrderLineList, AnalyzeReasonsSEO.INSUFFICIENT_SHIPMENT, processDO);
                }
            }
        } catch (Exception e) {
            String lastMessage = "获取起运量失败";
            processDO.sysErrorLog("起运量检查", lastMessage, null, e);
            processDO.setLastError(lastMessage);
            log.error("ForecastOrderLineAbility$check_shipment_volume_failed");
            throw new Exception(lastMessage);
        }
    }

    /**
     * 校验计划量
     *
     * @return
     */
    @FunctionInvoker
    public void checkPlanQuantity(AnalyzeProcessDO processDO) throws Exception {
        if (CollectionUtils.isEmpty(processDO.getOrderLineListToAnalyze())) {
            return;
        }
        List<ForecastOrderLineSDO> orderLineListToAnalyze = processDO.getOrderLineListToAnalyze();
        List<ForecastOrderLineSDO> lineListToFinish = new ArrayList<>(orderLineListToAnalyze);

        //  性能优化点1 构建map暂存orderline-planId的缓存信息 已完成
        Map</* orderId */ String, /* planId */ String> orderLineCachePlanIdMap = Maps.newHashMap();
        Map</* orderId */ String, ForecastOrderSDO> forecastOrderMap = processDO.getForecastOrderMap();


        for (ForecastOrderLineSDO forecastOrderLineSDO : lineListToFinish) {
            try {
                String orderId = forecastOrderLineSDO.getMainOrderId();
                ForecastOrderSDO forecastOrderSDO = forecastOrderMap.get(orderId);
                String planId = orderLineCachePlanIdMap.get(orderId);
                if (StringUtils.isEmpty(planId)) {
                    planId = strategyCalculate.queryParentId(forecastOrderSDO.getOrganizationCode());
                    orderLineCachePlanIdMap.put(orderId, planId);
                }

                //  TODO 性能优化点2 根据查询（可用量）这里可性能调优，月度计划明细可以一次拉取，而非通过ES逐条查询，这样可以减少网络耗时
                Integer integer = strategyCalculate.pagePlanNumberV2(forecastOrderLineSDO, planId, forecastOrderSDO);
                if (integer != -1 && integer < forecastOrderLineSDO.getPredictReplyAmount()) {
//                    finishOneOrderLineForOverMonthVolume(forecastOrderLineSDO);
                    finishOneOrderLineForReason(forecastOrderLineSDO, AnalyzeReasonsSEO.OVER_MONTH_VOLUME, processDO);
                }
            } catch (Exception e) {
                String lastMessage = "月度计划校验失败";
                log.error("ForecastOrderLineAbility$check_plan_quantity_failed", e);
                throw new Exception(lastMessage);
            }
        }
    }

    /**
     * 检查信贷
     *
     * @return
     */
    @FunctionInvoker
    public void checkCredit(AnalyzeProcessDO processDO, List<ForecastOrderLineSDO> batchAnalysisOrdersRequest) throws Exception {
        if (CollectionUtils.isEmpty(processDO.getOrderLineListToAnalyze()) && CollectionUtils.isEmpty(processDO.getOrderLineListAnalyzedDone())) {
            return;
        }

        List<ForecastOrderLineSDO> orderLineListToAnalyze = processDO.getOrderLineListToAnalyze();
        List<ForecastOrderLineSDO> orderLineListAnalyzedDone = processDO.getOrderLineListAnalyzedDone();
        List<ForecastOrderLineSDO> toDoList = new ArrayList<>();

        //copy activity info to run-time orderLine
        Map<Long, List<ForecastOrderLineSDO>> activityEffectObject = Nullable.stream(batchAnalysisOrdersRequest).collect(Collectors.groupingBy(ForecastOrderLineSDO::getId));
        for (ForecastOrderLineSDO forecastOrderLineSDO : orderLineListToAnalyze) {
            if (activityEffectObject.containsKey(forecastOrderLineSDO.getId())) {
                ForecastOrderLineSDO activityOne = activityEffectObject.get(forecastOrderLineSDO.getId()).get(0);
                forecastOrderLineSDO.setActivityPrice(activityOne.getActivityPrice());
                forecastOrderLineSDO.setActivityName(activityOne.getActivityName());
                forecastOrderLineSDO.setActivityCode(activityOne.getActivityCode());
                forecastOrderLineSDO.setTotalActivityFee(activityOne.getTotalActivityFee());
                forecastOrderLineSDO.setActivityNumber(activityOne.getActivityNumber());
                forecastOrderLineSDO.setActivityType(activityOne.getActivityType());
                forecastOrderLineSDO.setFeatures(activityOne.getFeatures());
            }
        }

        for (ForecastOrderLineSDO forecastOrderLineSDO : orderLineListAnalyzedDone) {
            if (activityEffectObject.containsKey(forecastOrderLineSDO.getId())) {
                ForecastOrderLineSDO activityOne = activityEffectObject.get(forecastOrderLineSDO.getId()).get(0);
                forecastOrderLineSDO.setActivityPrice(activityOne.getActivityPrice());
                forecastOrderLineSDO.setActivityName(activityOne.getActivityName());
                forecastOrderLineSDO.setActivityCode(activityOne.getActivityCode());
                forecastOrderLineSDO.setTotalActivityFee(activityOne.getTotalActivityFee());
                forecastOrderLineSDO.setActivityNumber(activityOne.getActivityNumber());
                forecastOrderLineSDO.setActivityType(activityOne.getActivityType());
                forecastOrderLineSDO.setFeatures(activityOne.getFeatures());
            }
        }

        if (CollectionUtils.isNotEmpty(processDO.getOrderLineListAnalyzedDone())){
            toDoList.addAll(processDO.getOrderLineListAnalyzedDone());
        }
        if (CollectionUtils.isNotEmpty(processDO.getOrderLineListToAnalyze())){
            toDoList.addAll(processDO.getOrderLineListToAnalyze());
        }

        //根据主订单进行分组
        Map<String, List<ForecastOrderLineSDO>> mainIdGroupByMap = Nullable.stream(toDoList).
                collect(Collectors.groupingBy(ForecastOrderLineSDO::getMainOrderId));
        //主单ID->主单总计Map
        Map<String,Long> mainTotalAmountMap=new HashMap<>();
        //批量查询信贷入参
        List<CreditQueryRequest> creditQueryRequestList=new ArrayList<>();
        //销售组织ID-客户编码-业务渠道-货品组->信贷余额
        Map<String,Long> creditMap=new HashMap<>();
        //预报主单ID->主单主单
        Map<String, ForecastOrderSDO> forecastOrderMap = processDO.getForecastOrderMap();
        //销售组织ID-客户编码-业务渠道-货品组->预报主单集合
        Map<String, List<ForecastOrderSDO>> customerMainOrderMap = forecastOrderMap.values().stream().collect(Collectors.groupingBy(o ->
                String.format("%s-%s-%s-%s", o.getOrganizationCode(), o.getCustomerCode(), o.getChannelCode(), o.getProductGroupCode())));

        for (String mainOrderId : mainIdGroupByMap.keySet()) {
            //查询已经回复订单的总价
            List<ForecastOrderLineSDO> forecastOrderLineSDOList = mainIdGroupByMap.get(mainOrderId);

            ForecastOrderSDO forecastOrderInfo = forecastOrderMap.getOrDefault(mainOrderId, new ForecastOrderSDO());
            long sum = Nullable.stream(forecastOrderLineSDOList).mapToLong(it -> {
                if (Objects.nonNull(it.getUnitPrice())) {
                    //【0102需求】计算订单金额考虑活动价格
                    Long orderLinePrice = Objects.isNull(it.getActivityPrice()) ? it.getUnitPrice() : it.getActivityPrice();
                    orderLinePrice = orderLinePrice <= 0 ? it.getUnitPrice() : orderLinePrice;
                    return it.getPredictReplyAmount() * orderLinePrice ;
                }
                return 0;
            }).sum();
            mainTotalAmountMap.put(mainOrderId, sum);

            CreditQueryRequest creditQueryRequest = ForecastOrderRepositoryConverter.buildCreditQueryRequest(forecastOrderInfo);
            creditQueryRequestList.add(creditQueryRequest);
        }
        Date date = new Date();
        //批量查询信贷
        try {
            //去重同条件信贷查询
            batchQueryCreditList(creditQueryRequestList, creditMap);
        } catch (Throwable e) {
            String lastMessage = "信用检查失败";
            log.error("ForecastOrderLineAbility$check_credit_failed", e);
            throw new RuntimeException(lastMessage);
        }
        Date date2 = new Date();
        log.info("信贷批量查询耗时{}",date2.getTime()-date.getTime());

        for (Map.Entry<String, List<ForecastOrderSDO>> customerOrderEntry : customerMainOrderMap.entrySet()) {
            List<ForecastOrderSDO> sameCustomerOrderList = customerOrderEntry.getValue();
            List<ForecastOrderSDO> sortedOrderList = sameCustomerOrderList.stream().sorted(Comparator.comparing(ForecastOrderSDO::getOrderBizId)).collect(Collectors.toList());
            for (ForecastOrderSDO forecastOrderSDO : sortedOrderList) {
                String mainOrderId = forecastOrderSDO.getId();
                Long sum = mainTotalAmountMap.getOrDefault(mainOrderId, 0L);
                List<ForecastOrderLineSDO> forecastOrderLineSDOList = mainIdGroupByMap.get(mainOrderId);
                String creditKey = String.format("%s-%s-%s-%s", forecastOrderSDO.getOrganizationCode(),  forecastOrderSDO.getCustomerCode(), forecastOrderSDO.getChannelCode(),
                        forecastOrderSDO.getProductGroupCode());
                Long balance = creditMap.get(creditKey);
                if (balance != null ) {
                    if ( sum > balance){
                        finishOrderLineListForReason(forecastOrderLineSDOList, AnalyzeReasonsSEO.INSUFFICIENT_ACCOUNTS, processDO);
                    }else {
                        //扣减余额
                        balance=balance-sum;
                        creditMap.put(creditKey,balance);
                    }
                }
            }
        }

        //汇总分析会影响部分 dealStatus=1的进入doneList
        if (CollectionUtils.isNotEmpty(processDO.getOrderLineListAnalyzedDone())) {
            processDO.getOrderLineListAnalyzedDone().forEach(o -> {
                        o.setAnalysisTime(new Date());
                       // toDoList.removeIf(next -> o.getId().equals(next.getId()));
                        //完成的单据写入数据库
                        saveDealStatusToAnalyzed(o, processDO);
                    }

            );
        }

    }


    /**
     * 检查活动
     *
     * @param
     * @return
     */
    public void checkActivity(List<ForecastOrderLineSDO> orderLineList,boolean isOnlyJoinWithoutActivity) throws Exception {
        if (CollectionUtils.isEmpty(orderLineList)) {
            return;
        }
        List<String> orderLineIdList = Nullable.stream(orderLineList).map(it -> it.getId().toString()).collect(Collectors.toList());
        ForecastOrderActivityRequest forecastOrderActivityRequest = ForecastOrderActivityRequest.builder().ids(orderLineIdList).build();
        Map<Long, List<ForecastOrderLineSDO>> inputOrderMap = Nullable.stream(orderLineList).collect(Collectors.groupingBy(ForecastOrderLineSDO::getId));
        //根据传入Ids进行单据参数封装  更改订单处理状态
        try {
            List<ForecastOrderEntity> forecastOrderEntities = ForecastOrderFactory.checkActivityForecastMemory(orderLineList);
            boolean isSuccess = true;
            for (ForecastOrderEntity forecastOrderEntity : forecastOrderEntities) {
                //2022.10.31 当仅加入队列时  且不是补货活动订单 跳过
                if (!isOnlyJoinWithoutActivity|| ForecastOrderTag.RESTOCK_FORECAST.getValue().equals(forecastOrderEntity.getForecastOrderSDO().getOrderType())){
                    //按照主单维度进行查询活动
                    forecastOrderEntity.activityUpdateDealForecast(JSONObject.parseObject(JSONObject.toJSONString(forecastOrderActivityRequest.getApiContext()),
                            ApiContext.class));
                }else {
                    //仅加入队列 无活动 需要去除活动标记
                    log.info("analysisDeal onlyJoinWithoutActivity skip checkActivity, remove activity mark");
                    removeActivityMark(forecastOrderEntity.getForecastOrderSDO().getForecastOrderLineSDOList());
                }


                //为了分析值传递
                if (CollectionUtils.isNotEmpty(forecastOrderEntity.getForecastOrderSDO().getForecastOrderLineSDOList())) {
                    for (ForecastOrderLineSDO forecastOrderLineSDO : forecastOrderEntity.getForecastOrderSDO().getForecastOrderLineSDOList()) {
                        if (inputOrderMap.containsKey(forecastOrderLineSDO.getId())) {
                            ForecastOrderLineSDO inputObject = inputOrderMap.get(forecastOrderLineSDO.getId()).get(0);

                            inputObject.setActivityPrice(forecastOrderLineSDO.getActivityPrice());
                            inputObject.setActivityName(forecastOrderLineSDO.getActivityName());
                            inputObject.setActivityCode(forecastOrderLineSDO.getActivityCode());
                            inputObject.setTotalActivityFee(forecastOrderLineSDO.getTotalActivityFee());
                            inputObject.setActivityNumber(forecastOrderLineSDO.getActivityNumber());
                            inputObject.setActivityType(forecastOrderLineSDO.getActivityType());
                            inputObject.setFeatures(forecastOrderLineSDO.getFeatures());
                        }
                    }
                }

                //将数据的信息插入分析队列的表中
                //TODO:wm确认业务逻辑
                List<DealLineSDO> collect = Nullable.stream(forecastOrderEntity.getForecastOrderSDO().getForecastOrderLineSDOList()).map(forecastOrderLineSDO -> {
                    DealLineSDO dealLineSDO = new DealLineSDO();
                    dealLineSDO.setForecastOrderLineId(forecastOrderLineSDO.getId().toString());
                    dealLineSDO.setDealStatus(ForecastDealStatusSEO.CHECk_ED.getStatus());
                    dealLineSDO.setDealType(ForecastDealTypeSEO.CHECK_ACTIVITIES.getDealTypeCode().toString());
                    dealLineSDO.setModifierName(forecastOrderLineSDO.getModifierName());
                    dealLineSDO.setModifierId(forecastOrderLineSDO.getModifierId());
                    dealLineSDO.setDealTime(new Date());
                    Date now = new Date();
                    SimpleDateFormat f = new SimpleDateFormat("yyyyMMddhhmmss");
                    String format = f.format(now);//20211015030520
                    dealLineSDO.setBatchNumber(format);
                    return dealLineSDO;
                }).collect(Collectors.toList());
                //创建分析队列的表中数据
                boolean b = dealLineWriteRepository.batchCreate(collect);
            }

        } catch (Exception e) {
            String lastMessage = "检查活动异常";
            log.error("ForecastOrderLineAbility$check_activity_failed");
            e.printStackTrace();
            throw new Exception(lastMessage);
        }
    }

    /**
     * 重构后的货品分配方法
     * 将货品按照每日库存、优先策略分配到子单上
     * 1、分配过程采用库存单位，保存到子单预计回复数量时，转换为销售单位
     * 2、分配完成后，不能满足预报量的，设置为库存不足，并完成分析，不再进行后续处理
     */
    @FunctionInvoker
    //TODO：修改为返回值
    public void distributeItemInventory(AnalyzeProcessDO processDO) throws Exception {
        if (CollectionUtils.isEmpty(processDO.getOrderLineListToAnalyze())) {
            return;
        }
        List<ForecastOrderLineSDO> orderLineListToAnalyze = processDO.getOrderLineListToAnalyze();
        //先仓库分组 再根据货品分组
        Map<String, List<ForecastOrderLineSDO>> orderLineGroupByHouse = Nullable.stream(orderLineListToAnalyze)
                .collect(Collectors.groupingBy(ForecastOrderLineSDO::getWarehouseArea));
        try {
            //根据仓库进行处理
            for (Map.Entry<String, List<ForecastOrderLineSDO>> wareHouseAndOrderLineEntry : orderLineGroupByHouse.entrySet()) {
                //发货仓code
                String warehouseAreaCode = wareHouseAndOrderLineEntry.getKey();
                //这些订单都是归属同一个主单、同一个仓库  TBD ？ 这里不一定是同一个主单
                List<ForecastOrderLineSDO> forecastOrderLineSDOList = wareHouseAndOrderLineEntry.getValue();
                //根据货品进行分组
                Map<String, List<ForecastOrderLineSDO>> itemAndOrderLineListMap = Nullable.stream(forecastOrderLineSDOList)
                        .collect(Collectors.groupingBy(ForecastOrderLineSDO::getScItemId));
                //货品进行分配available
                for (Map.Entry<String, List<ForecastOrderLineSDO>> itemAndOrderLineListEntry : itemAndOrderLineListMap.entrySet()) {
                    //同一仓库同一货品的订单
                    List<ForecastOrderLineSDO> orderLineList = itemAndOrderLineListEntry.getValue();
                    if (CollectionUtils.isEmpty(orderLineList)) {
                        continue;
                    }
                    List<ForecastOrderLineSDO> needCheckInventoryLineList=new ArrayList<>();
                    inventorySkipStrategy(processDO, orderLineList, needCheckInventoryLineList);
                    //库存是小数
                    BigDecimal availableInventory = BigDecimal.valueOf(queryGoodsInventory(warehouseAreaCode, processDO, itemAndOrderLineListEntry.getKey()));
                    //没有库存，所有子单全部设置为库存不足，完成分析
                    if (availableInventory.compareTo(BigDecimal.ZERO) <= 0) {
                        String lastMessage = "| 可用库存: " + availableInventory;
                        processDO.setLastMessage(lastMessage);

                        finishOrderLineListForReason(needCheckInventoryLineList, AnalyzeReasonsSEO.INVENTORY_SHORTAGE, processDO);
                        continue;
                    }
                    int sumQuantityInSalesUnit = getSumForecastQuantity(needCheckInventoryLineList);
                    if (sumQuantityInSalesUnit <= 0) {
                        continue;
                    }
                    //将有限库存按照规则分配给子单
                    distributeItemInventoryToOrderLines(needCheckInventoryLineList, processDO, availableInventory);

                }
            }
        } catch (Throwable e) {
            String lastMessage = "库存分解失败";
            processDO.sysErrorLogInDistributeInventory(lastMessage, null, e);
            processDO.setLastError(lastMessage);
            log.error("ForecastOrderLineAbility$distribute_inventory_to_orderline_failed,"+e.getMessage());
            log.error("ForecastOrderLineAbility$distribute_inventory_to_orderLine_failed,",e);
            e.printStackTrace();
            throw new Exception(lastMessage);
        }
    }

    private void inventorySkipStrategy(AnalyzeProcessDO processDO, List<ForecastOrderLineSDO> orderLineList, List<ForecastOrderLineSDO> needCheckInventoryLineList) {
        Map<String, List<ForecastOrderLineSDO>> groupByMainIdMap = Nullable.stream(orderLineList).collect(Collectors.groupingBy(ForecastOrderLineSDO::getMainOrderId));
        for (Map.Entry<String, List<ForecastOrderLineSDO>> groupEntry : groupByMainIdMap.entrySet()) {
            boolean needCheckInventory=true;
            List<ForecastOrderLineSDO> orderLineSDOS = groupEntry.getValue();
//            ForecastOrderSDO forecastOrderSDO = this.forecastOrderMap.get(groupEntry.getKey());
            ForecastOrderSDO forecastOrderSDO = processDO.getForecastOrderMap().get(groupEntry.getKey());
            String organizationCode = forecastOrderSDO.getOrganizationCode();
            StrategyPageQueryRequest moreConditionQueryRequest = new StrategyPageQueryRequest();
            moreConditionQueryRequest.setType(StrategyTypeEnum.INVENTORY_VALID.getCode());
            moreConditionQueryRequest.setOrganizationCode(organizationCode);//forecastOrderSDO.getOrganizationCode()
            try {
                Result<List<StrategyPageQueryResponse>> moreConditionQueryResponseResult = strategyQueryService.pageQuery(moreConditionQueryRequest);
                if (Objects.nonNull(moreConditionQueryResponseResult)&&CollectionUtils.isNotEmpty(moreConditionQueryResponseResult.getResult())){
                    for (ForecastOrderLineSDO forecastOrderLineSDO : orderLineSDOS) {
                        int shouldFilledQuantity = Integer.parseInt(forecastOrderLineSDO.getQuantity()) - Nullable.of(forecastOrderLineSDO.getActualTotalReplyAmount()).orElse(0);
                        forecastOrderLineSDO.setPredictReplyAmount(shouldFilledQuantity);
                        String message = "|库存校验策略为不校验"+"| 结果: 满足预报量";
                        processDO.bizAnalyzeLogWithDistributeInventory(forecastOrderLineSDO, message);
                    }
                    log.info("预报订单分析库存分配，预报单:{},命中库存不检查策略:{},全部满足预计回复数量",forecastOrderSDO.getOrderBizId(),moreConditionQueryResponseResult.getResult().get(0).getStrategyName());
                    needCheckInventory=false;
                }
            }catch (Exception e){
                e.printStackTrace();

            }

            if (needCheckInventory){
                needCheckInventoryLineList.addAll(orderLineSDOS);
            }
        }
    }

    /**
     * 重构后的分配库存方法
     * 1、分析过程为库存单位
     *
     * @param forecastOrderLineToDistribute 单个货品的子单行列表
     * @param processDO                     处理内容封装
     * @param availableInventory            可分配的库存
     */
    private void distributeItemInventoryToOrderLines(List<ForecastOrderLineSDO> forecastOrderLineToDistribute,
                                                     AnalyzeProcessDO processDO,
                                                     BigDecimal availableInventory) {
        //按照每日库存分配
        //TODO:WM将库存划分为每日库存和非每日库存，分别进行分配
        //待处理队列会再各个分配环境中移除，分配库存后，从待处理队列中移除
        availableInventory = distributeItemInventoryByDailyInventory(forecastOrderLineToDistribute, processDO, availableInventory);
        //每日库存分解完成后，没有库存，就不需要再对其他子单进行处理了
        if (availableInventory.compareTo(BigDecimal.ZERO) <= 0) {
            return;
        }
        //按照优先级策略进行分配，
        availableInventory = distributeItemInventoryByPriorityStrategy(forecastOrderLineToDistribute, processDO, availableInventory);
        if (availableInventory.compareTo(BigDecimal.ZERO) <= 0) {
            return;
        }
        //如果库存仍有剩余，按照提交时间顺序分配给剩余子单
        newDistributeItemInventoryByCreateTime(forecastOrderLineToDistribute, processDO, availableInventory);
    }

    /**
     * 按照子单的每日库存分货品的配库存到子单
     * 1、每日库存已分配的单子就不进行后续的处理了
     *
     * @param orderLineListToDistribute 待处理的子单列表
     * @param availableInventory        可分解的库存
     * @return 分解后的可用库存
     */
    private BigDecimal distributeItemInventoryByDailyInventory(List<ForecastOrderLineSDO> orderLineListToDistribute,
                                                               AnalyzeProcessDO processDO,
                                                               BigDecimal availableInventory) {
        if (CollectionUtils.isEmpty(orderLineListToDistribute) || availableInventory.compareTo(BigDecimal.ZERO) <= 0) {
            return availableInventory;
        }

        List<ForecastOrderLineSDO> orderLineListByItem;

        //判断有效性
        orderLineListByItem = orderLineListToDistribute.stream().map(this::validityCheck).collect(Collectors.toList());

        Map<Object, List<ForecastOrderLineSDO>> validOrderLineMap = Nullable.stream(orderLineListByItem)
                .collect(Collectors.groupingBy(it -> it.getFeatures().get(ForecastConstants.CHECK_EFFECTIVENESS)));

        // 无效的子订单没有处理，没有出队列？
        List<ForecastOrderLineSDO> validOrderLineList = validOrderLineMap.get("true");
        if (Objects.isNull(validOrderLineList)) {
            String message = "| 结果: 满足预报量";
            processDO.setLastMessage(message);
            processDO.bizAnalyzeLogWithDistributeInventory(validOrderLineList);
            return availableInventory;
        }
        orderLineListToDistribute.removeAll(validOrderLineList);
        //将有每日库存的子单从后续处理的列表中移除，不需要再进行处理
        if (CollectionUtils.isEmpty(validOrderLineList)) {
            return availableInventory;
        }
        //每日库存有效的子单，按照时间进行排序
        try {
            List<ForecastOrderLineSDO> sortedByGmtCreateOrderLineList = Nullable.stream(validOrderLineList)
                    .sorted(Comparator.comparing(ForecastOrderLineSDO::getId)).collect(Collectors.toList());
            for (ForecastOrderLineSDO orderLine : sortedByGmtCreateOrderLineList) {
                if (availableInventory.compareTo(BigDecimal.ZERO) <= 0 || Objects.isNull(orderLine.getFeatures())
                        || Objects.isNull(orderLine.getFeatures().get(DISINTEGRATION_QTY))) {
                    processDO.setLastMessage("可用库存为0或获取每日库存失败");
                    finishOneOrderLineForReason(orderLine, AnalyzeReasonsSEO.INVENTORY_SHORTAGE, processDO);

                    continue;
                }
                //是否已经满足
                int diffQuantity = newGetDiffQuantityInOrderLine(orderLine);
                if (diffQuantity <= 0) {
//                    finishOneOrderLineForInventoryShortage(orderLine);
                    continue;
                    // TBD ? 这里不重新分析？库存分配的可能已经失效了？
                }
                //每日库存
                BigDecimal dailyInventoryOfOrderLine;
                try {
                    //处理步骤：2.根据每日库存分配，子单的每日仓库占用额，在创建预报单时写入
                    dailyInventoryOfOrderLine =new BigDecimal(String.valueOf(orderLine.getFeatures().get(DISINTEGRATION_QTY)));
                    //每日库存为0
                    // TBD ? 在创建异常时，这个数字也可能为0，那就需要重新进行分析 @see ForecastOrderEntity:400
                    if (dailyInventoryOfOrderLine.compareTo(BigDecimal.ZERO) == 0) {
                        String lastMessage = " | 分配每日库存: " + dailyInventoryOfOrderLine + " | 结果：每日库存为0";
                        processDO.setLastMessage(lastMessage);
                        finishOneOrderLineForReason(orderLine, AnalyzeReasonsSEO.INVENTORY_SHORTAGE, processDO);

                        continue;
                    }
                } catch (Exception e) {
                    String lastError = "获取每日库存配额失败";
                    processDO.setLastError(lastError);
                    e.printStackTrace();

                    log.error("获取每日库存配额失败", e);
                    throw e;
                }
                //差量
                BigDecimal diffQuantityByInventoryUnit =convertSalesUnitToInventoryUnit(orderLine, diffQuantity);
                //每日库存单位转换关系
                BigDecimal diffDailyQuantityByInventoryUnit = convertSalesUnitToInventoryUnitFloat(orderLine, dailyInventoryOfOrderLine);
                //都是库存单位
                BigDecimal minQuantity = mininumOfThreeDouble( diffQuantityByInventoryUnit, availableInventory, diffDailyQuantityByInventoryUnit);
                //如果差量最小，全部满足预报量
                if (diffQuantityByInventoryUnit.compareTo(minQuantity)==0) {
                    int shouldFilledQuantity = Integer.parseInt(orderLine.getQuantity()) - Nullable.of(orderLine.getActualTotalReplyAmount()).orElse(0);
                    orderLine.setPredictReplyAmount(shouldFilledQuantity);
                    String message = "| 预报差量: " + diffQuantityByInventoryUnit + "| 可用库存: " + availableInventory + "| 每日库存: " +
                            dailyInventoryOfOrderLine + "| 结果: 满足预报量";
                    processDO.bizAnalyzeLogWithDistributeInventory(orderLine, message);
                } else {
                    //否则满足最小量，并设置为库存不足
                    orderLine.setPredictReplyAmount(orderLine.getPredictReplyAmount() + convertInventoryUnitToSaleUnit(orderLine, minQuantity));
                    String lastMessage = "| 预报差量: " + diffQuantityByInventoryUnit + "| 可用库存: " + availableInventory + "| 每日库存: " +
                            dailyInventoryOfOrderLine + "| 结果: 库存不足";
                    processDO.setLastMessage(lastMessage);
                    finishOneOrderLineForReason(orderLine, AnalyzeReasonsSEO.INVENTORY_SHORTAGE, processDO);
                }
                availableInventory = availableInventory.subtract(minQuantity);
            }
        } catch (Exception e) {
            String lastError = "每日库存分配失败";
            processDO.setLastError(lastError);
            e.printStackTrace();
            log.error("每日库存分配失败", e);
            throw e;
        }

        return availableInventory;
    }

    /**
     * 根据优先级策略进行库存分解
     *
     * @param forecastOrderLineList 需要分解的子单列表，如果库存已经分解给子单，将从列表中移除
     * @param processDO             处理内容封装对象
     * @param availableInventory    可分解的库存
     * @return 分解后可用库存
     */
    private BigDecimal distributeItemInventoryByPriorityStrategy(List<ForecastOrderLineSDO> forecastOrderLineList,
                                                                 AnalyzeProcessDO processDO,
                                                                 BigDecimal availableInventory) {
        if (CollectionUtils.isEmpty(forecastOrderLineList) || availableInventory.compareTo(BigDecimal.ZERO) <= 0) {
            return availableInventory;
        }

        try {
            //按照时间排序
            List<ForecastOrderLineSDO> sortedInvalidOrderLineList = Nullable.stream(forecastOrderLineList)
                    .sorted(Comparator.comparing(ForecastOrderLineSDO::getId)).collect(Collectors.toList());

            //优先客户
            List<ForecastOrderLineSDO> orderLineByPriority = preferredCustomers(sortedInvalidOrderLineList, processDO);
            Map<Object, List<ForecastOrderLineSDO>> priorityOrderLineMap = Nullable.stream(orderLineByPriority)
                    .collect(Collectors.groupingBy(it -> it.getFeatures().get(ForecastConstants.CHECK_CUSTOMER)));

            //优先客户子订单
            List<ForecastOrderLineSDO> priorityCustomerList = Nullable.stream(priorityOrderLineMap.get("true"))
                    .sorted(Comparator.comparing(ForecastOrderLineSDO::getId)).collect(Collectors.toList());
            availableInventory = newBasicDistributeInventoryToOrderLineList(priorityCustomerList, availableInventory, processDO);
            if (availableInventory.compareTo(BigDecimal.ZERO) <= 0) {
                return availableInventory;
            }

            // FIXME ? ！！！后面处理应该要把列表里已经处理过的子订单排队掉

            //优先部门订单
            //处理步骤4.1：预报订单根据优先部门
            availableInventory = distributeItemInventoryByDepartmentPriority(sortedInvalidOrderLineList, availableInventory, processDO);
            if (availableInventory.compareTo(BigDecimal.ZERO) <= 0) {
                return availableInventory;
            }
            availableInventory = distributeItemInventoryByChannelPriority(sortedInvalidOrderLineList, availableInventory, processDO);
        } catch (Exception e) {

            String lastError = "根据策略分配库存失败";
            log.error("lastError", e);
            processDO.setLastError(lastError);
            e.printStackTrace();
            // TBD ? 弱依赖有问题
        }

        return availableInventory;
    }

    /**
     * 根据部门优先级分配库存
     *
     * @param forecastOrderLineList
     * @param availableInventory
     * @return
     */
    private BigDecimal distributeItemInventoryByDepartmentPriority(List<ForecastOrderLineSDO> forecastOrderLineList,
                                                                   BigDecimal availableInventory,
                                                                   AnalyzeProcessDO processDO) {
        if (CollectionUtils.isEmpty(forecastOrderLineList) || availableInventory.compareTo(BigDecimal.ZERO) <= 0) {
            return availableInventory;
        }

        //处理步骤4.1：预报订单根据优先部门
        List<ForecastOrderLineSDO> departmentList = preferredDepartment(forecastOrderLineList, processDO);
        Map<Object, List<ForecastOrderLineSDO>> groupDepartmentMap = Nullable.stream(departmentList)
                .collect(Collectors.groupingBy(it -> it.getFeatures().get(ForecastConstants.CHECK_DEPARTMENT)));
        //优先部门订单
        List<ForecastOrderLineSDO> priorityDepartmentOrderLineList = groupDepartmentMap.get("true");
        if (CollectionUtils.isEmpty(priorityDepartmentOrderLineList)) {
            return availableInventory;
        }

        int sumDiffQuantity = getSumForecastQuantity(priorityDepartmentOrderLineList);
        BigDecimal sumDiffQuantityOfInventoryUnit = convertSalesUnitToInventoryUnit(priorityDepartmentOrderLineList.get(0), sumDiffQuantity);
        //库存大于需求全量回复
        if (availableInventory.compareTo(sumDiffQuantityOfInventoryUnit) >= 0) {
            totallySatisfyOrderLineListInventory(priorityDepartmentOrderLineList, processDO);
            availableInventory = availableInventory.subtract(sumDiffQuantityOfInventoryUnit);
            return availableInventory;
        }

        //按照比例将库存分配给优先渠道
        availableInventory = distributeItemInventoryByDepartmentRatioNew(priorityDepartmentOrderLineList, availableInventory, processDO);

        return availableInventory;
    }

    /**
     * 【1226需求】按照优先部门比例将库存分配给子单
     *
     * @param forecastOrderLineListToDistribute 待分配的子单
     * @param availableInventory                可分配的库存
     * @return
     */
    //FIXME:需要与渠道抽出公共代码
    private BigDecimal distributeItemInventoryByDepartmentRatioNew(List<ForecastOrderLineSDO> forecastOrderLineListToDistribute,
                                                                   BigDecimal availableInventory,
                                                                   AnalyzeProcessDO processDO) {
        if (CollectionUtils.isEmpty(forecastOrderLineListToDistribute) || availableInventory.compareTo(BigDecimal.ZERO) <= 0) {
            return availableInventory;
        }
        //库存小于需求回复
        try {
            List<ForecastOrderLineSDO> lastRatio = strategyCalculate.allocationProportionDepartment(forecastOrderLineListToDistribute);
            List<ForecastOrderLineSDO> sortedLastRatio = Nullable.stream(lastRatio).sorted(Comparator.comparing(item ->
                    String.valueOf(item.getFeatures().get(ForecastConstants.CHECK_DEPARTMENT_PRIORITY)))).collect(Collectors.toList());

            //根据部门进行分组
            Map<String, List<ForecastOrderLineSDO>> departmentAndRatioMap = Nullable.stream(sortedLastRatio)
                    .collect(Collectors.groupingBy(it -> String.valueOf(it.getFeatures().get(ForecastConstants.DEPARTMENT_CODE))));

            //计算分组比例
            //FIXME:需要封装
            for (Map.Entry<String, List<ForecastOrderLineSDO>> entry : departmentAndRatioMap.entrySet()) {
                int sumQuantity = 0;
                BigDecimal departmentRatio = BigDecimal.ZERO;
                for (ForecastOrderLineSDO orderLine : entry.getValue()) {
                    Map features = orderLine.getFeatures();
                    departmentRatio = departmentRatio.add(new BigDecimal(String.valueOf(features.getOrDefault(ForecastConstants.LAST_RATIO,"0"))).
                            setScale(0, RoundingMode.DOWN));
                    sumQuantity += Integer.parseInt(orderLine.getQuantity());
                }

                for (ForecastOrderLineSDO orderLine : entry.getValue()) {
                    int quantity = Integer.parseInt(orderLine.getQuantity());
                    BigDecimal percent = BigDecimal.valueOf(quantity).divide(BigDecimal.valueOf(sumQuantity), 4, RoundingMode.DOWN);
                    BigDecimal orderLineRatio = percent.setScale(4, RoundingMode.DOWN).multiply(departmentRatio).divide(BigDecimal.valueOf(100), 4, RoundingMode.DOWN);
                    orderLine.getFeatures().put(ForecastConstants.LAST_RATIO, orderLineRatio.setScale(4, RoundingMode.DOWN).multiply(BigDecimal.valueOf(100)));
                }
            }

            availableInventory = distributeItemInventoryByRatio(forecastOrderLineListToDistribute, availableInventory,
                    sortedLastRatio, processDO, ForecastConstants.DEPARTMENT_CODE);
        } catch (Exception e) {
            log.error("按照部门分配库存失败");
            String lastError = "按照部门分配库存失败";
            processDO.setLastError(lastError);
            e.printStackTrace();
        }

        return availableInventory;
    }

    /**
     * 按照优先部门比例将库存分配给子单
     *
     * @param forecastOrderLineListToDistribute 待分配的子单
     * @param availableInventory                可分配的库存
     * @return
     */
    @Deprecated
    private BigDecimal distributeItemInventoryByDepartmentRatio(List<ForecastOrderLineSDO> forecastOrderLineListToDistribute,
                                                                BigDecimal availableInventory,
                                                                AnalyzeProcessDO processDO) {
        if (CollectionUtils.isEmpty(forecastOrderLineListToDistribute) || availableInventory.compareTo(BigDecimal.ZERO) <= 0) {
            return availableInventory;
        }
        //库存小于需求回复
        try {
            List<ForecastOrderLineSDO> lastRatio = strategyCalculate.allocationProportionDepartment(forecastOrderLineListToDistribute);
            List<ForecastOrderLineSDO> sortedLastRatio = Nullable.stream(lastRatio).sorted(Comparator.comparing(item ->
                    String.valueOf(item.getFeatures().get(ForecastConstants.CHECK_DEPARTMENT_PRIORITY)))).collect(Collectors.toList());

            availableInventory = distributeItemInventoryByRatio(forecastOrderLineListToDistribute, availableInventory,
                    sortedLastRatio, processDO, ForecastConstants.DEPARTMENT_CODE);
        } catch (Exception e) {
            log.error("按照部门分配库存失败");
            String lastError = "按照部门分配库存失败";
            processDO.setLastError(lastError);
            e.printStackTrace();
        }

        return availableInventory;
    }

    /**
     * 根据渠道优先级分配库存
     *
     * @param forecastOrderLineList 待分配的子单列表
     * @param availableInventory    可分配的库存
     * @return 剩余可分配库存
     */
    private BigDecimal distributeItemInventoryByChannelPriority(List<ForecastOrderLineSDO> forecastOrderLineList,
                                                                BigDecimal availableInventory,
                                                                AnalyzeProcessDO processDO) {
        if (CollectionUtils.isEmpty(forecastOrderLineList) || availableInventory.compareTo(BigDecimal.ZERO) <= 0) {
            return availableInventory;
        }

        List<ForecastOrderLineSDO> channelLineList = preferredChannel(forecastOrderLineList, processDO);
        Map<Object, List<ForecastOrderLineSDO>> groupChannelLineList = Nullable.stream(channelLineList)
                .collect(Collectors.groupingBy(it -> it.getFeatures().get(ForecastConstants.CHECK_CHANNEL)));

        //处理步骤4.2.1：优先渠道订单
        List<ForecastOrderLineSDO> preferredChannel = groupChannelLineList.get("true");
        if (CollectionUtils.isEmpty(preferredChannel)) {
            return availableInventory;
        }
        //优先渠道预报数量总数
        int totalRequiredInventory = getSumForecastQuantity(preferredChannel);

        //库存大于需求全量回复
        if (availableInventory.compareTo(BigDecimal.valueOf(totalRequiredInventory)) >= 0) {
            totallySatisfyOrderLineListInventory(preferredChannel, processDO);
            availableInventory = availableInventory.subtract(BigDecimal.valueOf(totalRequiredInventory));//确认回复量，并减去总量
            return availableInventory;
        }

        //按照比例将库存分配给优先渠道
        availableInventory = distributeItemInventoryByChannelRatioNew(preferredChannel, availableInventory, processDO);

        return availableInventory;
    }

    /**
     * 【1226需求】按照优先部门比例将库存分配给子单
     *
     * @param forecastOrderLineListToDistribute 待分配的子单
     * @param availableInventory                可分配的库存
     * @return
     */
    private BigDecimal distributeItemInventoryByChannelRatioNew(List<ForecastOrderLineSDO> forecastOrderLineListToDistribute,
                                                                BigDecimal availableInventory,
                                                                AnalyzeProcessDO processDO) {
        if (CollectionUtils.isEmpty(forecastOrderLineListToDistribute) || availableInventory.compareTo(BigDecimal.ZERO) <= 0) {
            return availableInventory;
        }
        //库存小于需求回复
        try {
            List<ForecastOrderLineSDO> lastRatio = strategyCalculate.allocationProportionChannel(forecastOrderLineListToDistribute);
            List<ForecastOrderLineSDO> sortedLastRatio = Nullable.stream(lastRatio).sorted(Comparator.comparing(item ->
                    String.valueOf(item.getFeatures().get(ForecastConstants.CHECK_CHANNEL_PRIORITY)))).collect(Collectors.toList());

            //根据部门进行分组
            Map<String, List<ForecastOrderLineSDO>> channelAndRatioMap = Nullable.stream(sortedLastRatio)
                    .collect(Collectors.groupingBy(it -> String.valueOf(it.getFeatures().get(ForecastConstants.SALECHANNEL_CODE))));

            //计算分组比例
            for (Map.Entry<String, List<ForecastOrderLineSDO>> entry : channelAndRatioMap.entrySet()) {
                int sumQuantity = 0;
                BigDecimal departmentRatio = BigDecimal.ZERO;
                for (ForecastOrderLineSDO orderLine : entry.getValue()) {
                    Map features = orderLine.getFeatures();
                    departmentRatio = departmentRatio.add(new BigDecimal(String.valueOf(features.getOrDefault(ForecastConstants.LAST_RATIO,"0"))).setScale(0, RoundingMode.DOWN));
                    sumQuantity += Integer.parseInt(orderLine.getQuantity());
                }

                for (ForecastOrderLineSDO orderLine : entry.getValue()) {
                    int quantity = Integer.parseInt(orderLine.getQuantity());
                    BigDecimal percent = BigDecimal.valueOf(quantity).divide(BigDecimal.valueOf(sumQuantity), 4, RoundingMode.DOWN);
                    BigDecimal orderLineRatio = percent.setScale(4, RoundingMode.DOWN).multiply(departmentRatio).divide(BigDecimal.valueOf(100), 4, RoundingMode.DOWN);
                    orderLine.getFeatures().put(ForecastConstants.LAST_RATIO, orderLineRatio.multiply(BigDecimal.valueOf(100)));
                }
            }

            availableInventory = distributeItemInventoryByRatio(forecastOrderLineListToDistribute, availableInventory, sortedLastRatio, processDO, ForecastConstants.SALECHANNEL_CODE);
        } catch (Exception e) {
            log.error("按照渠道分配库存失败");
            String lastError = "按照渠道分配库存失败";
            processDO.setLastError(lastError);
            e.printStackTrace();

        }

        return availableInventory;
    }

    /**
     * 按照优先渠道比例将库存分配给子单
     *
     * @param forecastOrderLineListToDistribute 待分配的子单
     * @param forecastOrderLineListToNextStep   需要继续处理的子单，如果子单已经完全满足，则从继续处理的子单中移除
     * @param availableInventory                可分配的库存
     * @return
     */
    @Deprecated
    private BigDecimal distributeItemInventoryByChannelRatio(List<ForecastOrderLineSDO> forecastOrderLineListToDistribute,
                                                             List<ForecastOrderLineSDO> forecastOrderLineListToNextStep,
                                                             BigDecimal availableInventory,
                                                             AnalyzeProcessDO processDO) throws Exception {
        try {
            if (CollectionUtils.isEmpty(forecastOrderLineListToDistribute) || availableInventory.compareTo(BigDecimal.ZERO) <= 0) {
                return availableInventory;
            }
            //库存小于需求回复
//        List<ForecastOrderLineSDO> predictChannelList = new ArrayList<>();

            List<ForecastOrderLineSDO> lastRatio = strategyCalculate.allocationProportionDepartment(forecastOrderLineListToDistribute);
            List<ForecastOrderLineSDO> sortedLastRatio = Nullable.stream(lastRatio).sorted(Comparator.comparing(item ->
                    String.valueOf(item.getFeatures().get(ForecastConstants.CHECK_CHANNEL_PRIORITY)))).collect(Collectors.toList());

            availableInventory = distributeItemInventoryByRatio(forecastOrderLineListToDistribute, availableInventory,
                    sortedLastRatio, processDO, ForecastConstants.SALECHANNEL_CODE);
        } catch (Exception e) {
            String lastMessage = "按照渠道比例分配库存异常";
            log.error(lastMessage, e);
            throw new Exception(lastMessage);
        }

        return availableInventory;
    }

    /**
     * 按照一定的比例分配库存
     *
     * @param availableInventory
     * @param predictChannelList
     * @param sortedLastRatio
     * @return
     */
    @NotNull
    private BigDecimal distributeItemInventoryByRatio(List<ForecastOrderLineSDO> predictChannelList, BigDecimal availableInventory,
                                                      List<ForecastOrderLineSDO> sortedLastRatio, AnalyzeProcessDO processDO,
                                                      String channelOrDepartment) throws Exception {
        try {
            int sumReply = 0;
            int predictReplyCount = 0;
            for (ForecastOrderLineSDO orderLine : sortedLastRatio) {
                Object distributeRatio = orderLine.getFeatures().get(ForecastConstants.LAST_RATIO);//比例值
                if (Objects.isNull(distributeRatio)) {
                    continue;
                }
                //按比例计算后的库存
                BigDecimal scaledInventory = new BigDecimal(String.valueOf(distributeRatio)).
                        setScale(4, RoundingMode.DOWN).
                        multiply(availableInventory).
                        divide(BigDecimal.valueOf(100), 4, RoundingMode.DOWN);
                int diffQuantity = newGetDiffQuantityInOrderLine(orderLine);
                BigDecimal diffQuantityOfInventoryUnit = convertSalesUnitToInventoryUnit(orderLine, diffQuantity).setScale(2, RoundingMode.DOWN);
                //如果能满足差量
                if (diffQuantityOfInventoryUnit.compareTo(scaledInventory) <= 0) {
                    predictReplyCount = Integer.parseInt(orderLine.getQuantity()) - Nullable.of(orderLine.getActualTotalReplyAmount()).orElse(0);

                } else {
                    predictReplyCount = orderLine.getPredictReplyAmount() + convertInventoryUnitToSaleUnit(orderLine, scaledInventory);
                }
                //进行箱规转换
                try {
                    predictReplyCount = adjustPredictReplyCountBySpecsSwitch(orderLine, predictReplyCount, processDO);
                } catch (Exception e) {
                    log.error("箱规转换失败", e);
                }

                orderLine.setPredictReplyAmount(predictReplyCount);
                //分配额度，比例分配完成后再扣减库存，否则会影响后续分配数量
                sumReply += convertSalesUnitToInventoryUnit(orderLine, predictReplyCount).intValue();
            }

            //按比例分配完成后，【1225需求】剩余库存按照优先渠道顺序，渠道内按照子单预报量比例进行分配
            availableInventory = availableInventory.subtract(BigDecimal.valueOf(sumReply));
           /* List<ForecastOrderLineSDO> sortedOrderLineList;
            if (channelOrDepartment.equals(ForecastConstants.SALECHANNEL_CODE)) {
                sortedOrderLineList = sortedLastRatio.stream().sorted(Comparator.
                        comparing(ForecastOrderLineSDO::getMainOrderId).
                        thenComparing(o -> String.valueOf(o.getFeatures().get(ForecastConstants.CHECK_CHANNEL_PRIORITY)))).collect(Collectors.toList());

            } else {
                sortedOrderLineList = sortedLastRatio.stream().sorted(Comparator.
                        comparing(ForecastOrderLineSDO::getMainOrderId).
                        thenComparing(o -> String.valueOf(o.getFeatures().get(ForecastConstants.CHECK_DEPARTMENT_PRIORITY)))).collect(Collectors.toList());
            }*/
            if (availableInventory.compareTo(BigDecimal.ZERO) > 0) {
                for (ForecastOrderLineSDO orderLine : sortedLastRatio) {
                    if (availableInventory.compareTo(BigDecimal.ZERO) <= 0) {
                        finishOneOrderLineForReason(orderLine, AnalyzeReasonsSEO.INVENTORY_SHORTAGE, processDO);
                        continue;
                    }
                    int diffQuantity = newGetDiffQuantityInOrderLine(orderLine);
                    if (diffQuantity <= 0) {
                        continue;
                    }
                    BigDecimal diffQuantityOfInventoryUnit = convertSalesUnitToInventoryUnit(orderLine, diffQuantity);
                    if (availableInventory.compareTo(diffQuantityOfInventoryUnit) >= 0) {
                        try {
                            predictReplyCount = adjustPredictReplyCountBySpecsSwitch(orderLine, diffQuantity, processDO);
                        } catch (Exception e) {
                            log.error("箱规转换失败", e);
                        }

                        orderLine.setPredictReplyAmount(predictReplyCount + orderLine.getPredictReplyAmount());
                    } else {
                        predictReplyCount = convertInventoryUnitToSaleUnit(orderLine, newConvertInventoryUnitToSaleUnit(orderLine, availableInventory));
                        try {
                            predictReplyCount = adjustPredictReplyCountBySpecsSwitch(orderLine, predictReplyCount, processDO);
                        } catch (Exception e) {
                            log.error("箱规转换失败", e);
                        }
                        orderLine.setPredictReplyAmount(predictReplyCount + orderLine.getPredictReplyAmount());
                        finishOneOrderLineForReason(orderLine, AnalyzeReasonsSEO.INVENTORY_SHORTAGE, processDO);
                    }

                    //剩余量计算
                    availableInventory = availableInventory.subtract(convertSalesUnitToInventoryUnit(orderLine, predictReplyCount));
                    int adustInventory = convertInventoryUnitToSaleUnit(orderLine, availableInventory);
                    //不足一个运输单位，置为0
                    if (adjustPredictReplyCountBySpecsSwitch(orderLine, adustInventory, processDO) <= 0) {
                        availableInventory = BigDecimal.ZERO;
                    }
                }
            }
        } catch (Exception e) {
            String lastMessage = "按照比例分配库存异常";
            log.error(lastMessage, e);
            throw new Exception(lastMessage);
        }

        return availableInventory;
    }

    /**
     * 按照时间顺序分配库存给子单
     *
     * @param forecastOrderLineToDistribute 待分配的子单列表
     * @param availableInventory            可分配库存
     * @return 剩余可分配库存
     */
    private BigDecimal newDistributeItemInventoryByCreateTime(List<ForecastOrderLineSDO> forecastOrderLineToDistribute,
                                                              AnalyzeProcessDO processDO,
                                                              BigDecimal availableInventory) {
        if (Objects.isNull(forecastOrderLineToDistribute) || availableInventory.compareTo(BigDecimal.ZERO) <= 0) {
            return availableInventory;
        }
        //按照时间进行排序
        List<ForecastOrderLineSDO> sortedByGmtCreateOrderLineList = Nullable.stream(forecastOrderLineToDistribute)
                .sorted(Comparator.comparing(ForecastOrderLineSDO::getId)).collect(Collectors.toList());

        for (ForecastOrderLineSDO orderLine : sortedByGmtCreateOrderLineList) {
//
//            if (Objects.isNull(orderLine.getFeatures()) || Objects.isNull(orderLine.getFeatures().get(DISINTEGRATION_QTY))) {
//                continue;
//            }
            //如果已经没有剩余库存，子单状态置为库存不足，检查完成
            if (availableInventory.compareTo(BigDecimal.ZERO) <= 0) {
                finishOneOrderLineForReason(orderLine, AnalyzeReasonsSEO.INVENTORY_SHORTAGE, processDO);
                forecastOrderLineToDistribute.remove(orderLine);
            } else {
                int sumDiffQuantity = newGetDiffQuantityInOrderLine(orderLine);
                BigDecimal sumDiffQuantityOfInventoryUnit = convertSalesUnitToInventoryUnit(orderLine, sumDiffQuantity);
                if (availableInventory.compareTo(sumDiffQuantityOfInventoryUnit) >= 0) {
                    int shouldFiledQuantity = Integer.parseInt(orderLine.getQuantity()) - Nullable.of(orderLine.getActualTotalReplyAmount()).orElse(0);
                    orderLine.setPredictReplyAmount(shouldFiledQuantity);
                    availableInventory = availableInventory.subtract(sumDiffQuantityOfInventoryUnit);
                } else {
                    orderLine.setPredictReplyAmount(orderLine.getPredictReplyAmount() + convertInventoryUnitToSaleUnit(orderLine, availableInventory));
                    finishOneOrderLineForReason(orderLine, AnalyzeReasonsSEO.INVENTORY_SHORTAGE, processDO);

                    availableInventory = BigDecimal.ZERO;
                }
            }
        }

        return availableInventory;
    }

    /**
     * 将库存分配给子单列表，按照传入子单列表的顺序
     * 1、如果库存不足，即完成分配，不会再参与后续库存分配
     *
     * @param orderLineToDistribute 需要分配的子单列表
     * @param availableInventory    可分配库存
     * @return 剩余库存
     */
    private BigDecimal newBasicDistributeInventoryToOrderLineList(List<ForecastOrderLineSDO> orderLineToDistribute,
                                                                  BigDecimal availableInventory,
                                                                  AnalyzeProcessDO processDO) {
        if (CollectionUtils.isEmpty(orderLineToDistribute) || availableInventory.compareTo(BigDecimal.ZERO) <= 0) {
            return availableInventory;
        }
        try {
            for (ForecastOrderLineSDO orderLine : orderLineToDistribute) {
                if (availableInventory.compareTo(BigDecimal.ZERO) <= 0) {
                    finishOneOrderLineForReason(orderLine, AnalyzeReasonsSEO.INVENTORY_SHORTAGE, processDO);
                    continue;
                }
                int diffQuantity = newGetDiffQuantityInOrderLine(orderLine);
                BigDecimal diffQuantityOfInventoryUnit = convertSalesUnitToInventoryUnit(orderLine, diffQuantity);
                //按照顺序满足预报差量
                if (availableInventory.compareTo(diffQuantityOfInventoryUnit) >= 0) {
                    int shouldFiledQuantity = Integer.parseInt(orderLine.getQuantity()) - Nullable.of(orderLine.getActualTotalReplyAmount()).orElse(0);
                    orderLine.setPredictReplyAmount(shouldFiledQuantity);
                    availableInventory = availableInventory.subtract(diffQuantityOfInventoryUnit);
                } else {
                    // 可用库存不足，将所有可用库存分给子单，并置为库存不足
                    orderLine.setPredictReplyAmount(orderLine.getPredictReplyAmount() + convertInventoryUnitToSaleUnit(orderLine, availableInventory));
                    finishOneOrderLineForReason(orderLine, AnalyzeReasonsSEO.INVENTORY_SHORTAGE, processDO);
                    availableInventory = BigDecimal.ZERO;//货品总数量置0，因为剩余货品已经分配给了当前子单
                }
            }
        } catch (Exception e) {
            String lastError = "按顺序分配库存失败";
            log.error("lastError", e);
            processDO.setLastError(lastError);
            e.printStackTrace();
            // TBD ? 异常后，后续的分配都是错的，应该要是强依赖
        }

        return availableInventory;
    }

    /**
     * 根据主单信息，查询起运量，单位为库存单位
     *
     * @param forecastOrderSDO 主单信息
     * @return 起运量，如果查询不到返回-1，
     */
    @FunctionInvoker
    public Integer newGetShipmentVolume(ForecastOrderSDO forecastOrderSDO, AnalyzeProcessDO processDO) {
        int shipmentVolume = -1;

        //查询起运量维度：组织、部门、渠道
        MoreConditionQueryRequest moreConditionQueryRequest = new MoreConditionQueryRequest();
        moreConditionQueryRequest.setStrategyType(StrategyTypeEnum.RISE_CARRY_AMOUNT.getCode());
        moreConditionQueryRequest.setOrganizationCode(forecastOrderSDO.getOrganizationCode());//forecastOrderSDO.getOrganizationCode()
        moreConditionQueryRequest.setDepartmentCode(forecastOrderSDO.getDepartmentCode());
        moreConditionQueryRequest.setChannelCode(forecastOrderSDO.getChannelCode());//forecastOrderSDO.getChannelCode()
//        log.info("查询起运量入参:" + JSONObject.toJSONString(moreConditionQueryRequest));
        try {
            Result<MoreConditionQueryResponse> moreConditionQueryResponseResult = strategyQueryService.moreConditionQuery(moreConditionQueryRequest);
//            log.info("查询起运量出参:" + JSONObject.toJSONString(moreConditionQueryResponseResult));
            if (Objects.nonNull(moreConditionQueryResponseResult)
                    && Objects.nonNull(moreConditionQueryResponseResult.getResult())
                    && Objects.nonNull(moreConditionQueryResponseResult.getResult().getStrategySDO())
                    && CollectionUtils.isNotEmpty(moreConditionQueryResponseResult.getResult().getStrategySDO().getDimensionSDOList())) {
                //只获取第1条记录？
                String ruleValue = moreConditionQueryResponseResult.getResult().getStrategySDO().getDimensionSDOList().get(0).getRuleValue();
                if (StringUtils.isNotBlank(ruleValue)) {
                    shipmentVolume = Integer.parseInt(ruleValue);
                    //用于记录业务日志
                    String lastMessage = "|【参数】策略类型:" + moreConditionQueryRequest.getStrategyType() +
                            "|组织编码:" + moreConditionQueryRequest.getOrganizationCode() +
                            "|部门编码:" + moreConditionQueryRequest.getDepartmentCode() +
                            "|渠道编码:" + moreConditionQueryRequest.getChannelCode() +
                            "|【结果】策略id:" + moreConditionQueryResponseResult.getResult().getStrategySDO().getId() +
                            "|维度id:" + moreConditionQueryResponseResult.getResult().getStrategySDO().getDimensionSDOList().get(0).getId() +
                            "|起运量:" + shipmentVolume;
                    processDO.setLastMessage(lastMessage);
                }
            }
        } catch (Exception e) {
            log.error("ERROR:查询起运量策略异常:" + e.getMessage(), e);
//            e.printStackTrace();
        }
        return shipmentVolume;
    }

    /**
     * 转换库存单位为销售单位
     *
     * @param inventoryQuantity 销售量
     * @return 销售量（库存单位）
     */
    private int convertInventoryUnitToSaleUnit(ForecastOrderLineSDO orderLine, BigDecimal inventoryQuantity) {
        try {
            String inventoryUnitValue = orderLine.getUnitConvert();
            int unitConvert = StringUtils.isNotBlank(inventoryUnitValue) ? Integer.parseInt(inventoryUnitValue) : 1;

            return  inventoryQuantity.multiply(BigDecimal.valueOf(unitConvert)).setScale(3,RoundingMode.HALF_UP).intValue();
        } catch (Exception e) {
            log.error("ERROR:Convert sales quantity to inventory quantity failed", e);
        }

        //转换异常，返回库存量
        return inventoryQuantity.intValue();
    }

    /**
     * 转换销售单位为库存单位
     * 1、向上取整，即不足一个库存单位，按照一个库存单位计算
     * 2、库存是小数
     *
     * @param salesQuantiy 销售量
     * @return 销售量（库存单位）
     */
    private BigDecimal convertSalesUnitToInventoryUnit(ForecastOrderLineSDO orderLine, Integer salesQuantiy) {
        try {
            String inventoryUnitValue = orderLine.getUnitConvert();
            int unitConvert = StringUtils.isNotBlank(inventoryUnitValue) ? Integer.parseInt(inventoryUnitValue) : 1;
            if (unitConvert == 0) {
                unitConvert = 1;
            }
            //向上取整
            // TBD ? 精度问题
            return BigDecimal.valueOf(salesQuantiy).divide(BigDecimal.valueOf(unitConvert),3,RoundingMode.HALF_UP) ;
        } catch (Exception e) {
            log.error("ERROR:Convert sales quantity to inventory quantity failed", e);
//            e.printStackTrace();
        }

        //转换异常，返回销售量
        return BigDecimal.valueOf(salesQuantiy);
    }

    /**
     * 转换销售单位为库存单位
     * 1、向上取整，即不足一个库存单位，按照一个库存单位计算
     * 2、库存是小数
     *
     * @param salesQuantity 销售量
     * @return 销售量（库存单位）
     */
    private BigDecimal convertSalesUnitToInventoryUnitFloat(ForecastOrderLineSDO orderLine, BigDecimal salesQuantity) {
        try {
            String inventoryUnitValue = orderLine.getUnitConvert();
            int unitConvert = StringUtils.isNotBlank(inventoryUnitValue) ? Integer.parseInt(inventoryUnitValue) : 1;
            if (unitConvert == 0) {
                unitConvert = 1;
            }
            //向上取整
            // TBD ? 精度问题
            return  salesQuantity.divide(BigDecimal.valueOf(unitConvert),3,RoundingMode.HALF_UP) ;
        } catch (Exception e) {
            log.error("ERROR:Convert sales quantity to inventory quantity failed", e);
//            e.printStackTrace();
        }

        //转换异常，返回销售量
        return salesQuantity.setScale(3,RoundingMode.HALF_UP);
    }

    /**
     * 使用BigDecimal进行转换库存单位为销售单位
     *
     * @param inventoryQuantity 销售量
     * @return 销售量（库存单位）
     */
    private BigDecimal newConvertInventoryUnitToSaleUnit(ForecastOrderLineSDO orderLine, BigDecimal inventoryQuantity) {
        try {
            String inventoryUnitValue = orderLine.getUnitConvert();
            int unitConvert = StringUtils.isNotBlank(inventoryUnitValue) ? Integer.parseInt(inventoryUnitValue) : 1;

            return inventoryQuantity.multiply(BigDecimal.valueOf(unitConvert));
        } catch (Exception e) {
            log.error("ERROR:Convert sales quantity to inventory quantity failed", e);
        }

        //转换异常，返回库存量
        return inventoryQuantity;
    }
    /**
     * 根据
     */

    /**
     * 根据箱规转换规则对预计回复量进行调整，调整为箱规的整数倍
     * 1、内含开关逻辑，如果关闭，则返回入参数量，如果打开，返回箱规转换后数量
     * 2、方法仅用于优先策略匹配过程中的按比例分配库存
     * 3、对输入的预计回复数量转换为运输规格，向上取整，再将运输规格转换为销售规格，将销售规格与预报量进行对比，如果大于预报量，运输规格数量减1后，再转换为销售规格，并返回
     *
     * @param predictReplyCount 预计回复量
     * @return 销售量（库存单位）
     */
    private int adjustPredictReplyCountBySpecsSwitch(ForecastOrderLineSDO orderLine, int predictReplyCount,
                                                     AnalyzeProcessDO processDO) throws Exception {
        Integer transportUnit;
        try {
            if (Objects.isNull(processDO) || Objects.isNull(processDO.getCartonSwitch())
                    || Objects.isNull(processDO.getTransportUnit())) {
                ForecastOrderSDO forecastOrderSDO = forecastOrderMap.get(orderLine.getMainOrderId());
                DimensionSDO dimensionSDO = strategyCalculate.strategyType(forecastOrderSDO.getOrganizationCode());

                if (Objects.isNull(dimensionSDO) ||
                        Objects.isNull(dimensionSDO.getExecuteAction()) ||
                        !dimensionSDO.getExecuteAction()) {
                    return predictReplyCount;
                }
                CartonSepcsRequest cartonSepcsRequest = new CartonSepcsRequest();
                cartonSepcsRequest.setScItemId(orderLine.getScItemId());
                Result<CartonSpecsSDO> cartonSpecsSDOResult = cartonSpecsQueryService.querySingle(cartonSepcsRequest);
                if (Objects.isNull(cartonSpecsSDOResult) ||
                        Objects.isNull(cartonSpecsSDOResult.getResult()) ||
                        Objects.isNull(cartonSpecsSDOResult.getResult().getSpecs())) {
                    return predictReplyCount;
                }
                transportUnit = cartonSpecsSDOResult.getResult().getSpecs();
                if (Objects.nonNull(processDO)) {
                    processDO.setCartonSwitch(dimensionSDO.getExecuteAction());
                    processDO.setTransportUnit(transportUnit);
                }
            }
            if (!processDO.getCartonSwitch()) {
                return predictReplyCount;
            }
            transportUnit = processDO.getTransportUnit();
            BigDecimal inventoryCount =convertSalesUnitToInventoryUnit(orderLine, predictReplyCount).
                    setScale(2, RoundingMode.DOWN);
            //向下取整
            Integer transportCount = inventoryCount.divide(BigDecimal.valueOf(transportUnit), 0, RoundingMode.DOWN).intValue();
            int adjustedPredictReplyCount = convertInventoryUnitToSaleUnit(orderLine, BigDecimal.valueOf((long) transportCount * transportUnit));
//            if (adjustedPredictReplyCount + orderLine.getPredictReplyAmount() > Integer.parseInt(orderLine.getQuantity())) {
//                transportCount--;
//                //正常情况下，入参传入合理，这里只需要减1次就可以满足要求
//                adjustedPredictReplyCount = convertInventoryUnitToSaleUnit(orderLine, transportCount * transportUnit);
//            }
            return adjustedPredictReplyCount;
        } catch (Exception e) {
            log.error("ERROR:Convert sales quantity to inventory quantity failed", e);
            throw new Exception("运输单位转换异常");
        }
    }

    /**
     * 获取子单列表中预报量的和
     *
     * @param orderLineList 子单列表
     * @return 所有子单预报量的和
     */
    private int getSumForecastQuantity(List<ForecastOrderLineSDO> orderLineList) {
        int sum = 0;
        for (ForecastOrderLineSDO orderLine : orderLineList) {
            sum += newGetDiffQuantityInOrderLine(orderLine);
        }

        return sum;
    }

    /**
     * 取3个int型最小值
     *
     * @param valueOne
     * @param valueTwo
     * @param valueThree
     * @return
     */
    private Integer mininumOfThreeInt(int valueOne, int valueTwo, int valueThree) {
        return Math.min(valueOne, Math.min(valueTwo, valueThree));
    }

    /**
     * 取3个BigDecimal型最小值
     *
     * @param valueOne
     * @param valueTwo
     * @param valueThree
     * @return
     */
    private BigDecimal mininumOfThreeDouble(BigDecimal valueOne, BigDecimal valueTwo, BigDecimal valueThree) {

        return valueOne.min(valueTwo.min(valueThree));
    }


    /**
     * 获取子单预报差额：预报量-累计回复量-预计回复量
     * 注：单位为销售单位
     *
     * @param forecastOrderLineSDO 子单信息
     * @return 分配差量
     */
    @FunctionInvoker
    public Integer newGetDiffQuantityInOrderLine(ForecastOrderLineSDO forecastOrderLineSDO) {
        if (Objects.isNull(forecastOrderLineSDO) || StringUtils.isEmpty(forecastOrderLineSDO.getQuantity())) {
            return 0;
        }
        //预报量
        try {
            int quantity = Integer.parseInt(forecastOrderLineSDO.getQuantity());
            int actualReplayAmount = Objects.isNull(forecastOrderLineSDO.getActualTotalReplyAmount()) ? 0 : forecastOrderLineSDO.getActualTotalReplyAmount();
            int predictReplayAmount = Objects.isNull(forecastOrderLineSDO.getPredictReplyAmount()) ? 0 : forecastOrderLineSDO.getPredictReplyAmount();
            return quantity - actualReplayAmount - predictReplayAmount;
        } catch (Exception e) {
            log.error("ERROR:Get diff count in order line failed:", e);
//            e.printStackTrace();
        }
        // TBD ? 异常后，是不是应该阻断流程，不应该给默认值0，会导致业务计算错误
        return 0;
    }

    /**
     * 设置子订单检查状态为不足起运量，并设置处理状态为分析完成
     * 注意：会直接保存到数据库中
     *
     * @param it
     */
    private void setOneOrderLineDealStatusWithReason(ForecastOrderLineSDO it, AnalyzeReasonsSEO reasons) {
        it.setAnalysisReason(reasons.getName());
        if (MapUtils.isNotEmpty(it.getFeatures())) {
            it.getFeatures().put(ForecastConstants.BULB_TYPE, reasons.getBulbType());
        } else {
            it.setFeatures(new HashMap() {{
                put(ForecastConstants.BULB_TYPE, reasons.getBulbType());
            }});
        }
        it.setDealStatus(ForecastDealStatusSEO.ANALYSIS_ED.getStatus());
        it.setAnalysisTime(new Date());

        //add by myw,20220829 ,reload activity feature to run object
//        ForecastOrderLineSDO query = forecastOrderLineQueryRepository.query(it.getId().toString());
//        if (Objects.nonNull(query)&&MapUtils.isNotEmpty(query.getFeatures())){
//            Map<String,Object> features = query.getFeatures();
//            Map<String,Object> runOrderLIneFeature = it.getFeatures();
//            for (Map.Entry<String, Object> entry : runOrderLIneFeature.entrySet()) {
//                features.put(entry.getKey(),entry.getValue());
//            }
//            it.setActivityCode(query.getActivityCode());
//            it.setActivityName(query.getActivityName());
//            it.setFeatures(features);
//        }
    }

    /**
     * 完成分析一个子单列表，原因是起运量不足
     * 完成动作：
     * 1、设置子单处理状态
     * 2、加入分析完成列表，从待分析列表中移除
     * 3、记录日志
     *
     * @param forecastOrderLineList 不足起运量的子单列表
     */
    @EpochTransactional(appCode = "ofs_forecast_sales")
    private void setOrderLineListDealStatusWithReason(List<ForecastOrderLineSDO> forecastOrderLineList,
                                                      AnalyzeReasonsSEO analyzeReeson,
                                                      AnalyzeProcessDO processDO) {
        for (ForecastOrderLineSDO orderLine : forecastOrderLineList) {
            setOneOrderLineDealStatusWithReason(orderLine, analyzeReeson);
            finishOneOrderLine(orderLine, processDO);
        }
        //TODO:修改日志
        processDO.bizAnalyzeLogWithCheckShipmentVolume("起运量不足", forecastOrderLineList);
    }


    /**
     * 将子单处理记录状态设置为已完成，即：生成子单处理记录并保存
     *
     * @param forecastOrderLineSDO 子单信息
     */
    private void saveDealStatusToAnalyzed(ForecastOrderLineSDO forecastOrderLineSDO, AnalyzeProcessDO processDO) {
        //处理队列操作
        try {
            DealLineSDO dealLineSDO = new DealLineSDO();
            //TODO FIX 这里不能取本地变量 processDO
            dealLineSDO.setBatchNumber(processDO.getBatchNo());
            dealLineSDO.setDealType(ForecastDealTypeSEO.BATCH_ANALYSIS.getDealTypeCode().toString());
            dealLineSDO.setForecastOrderLineId(forecastOrderLineSDO.getId().toString());
            Result<List<DealLineQueue>> listResult = dealLineQueryRepository.queryProgressQueueResult(dealLineSDO);

            //TODO FIX 这两条操作需放到一个事务内
            if (listResult != null && CollectionUtils.isNotEmpty(listResult.getResult())) {
                DealLineQueue dealLineQueue = listResult.getResult().get(0);
                dealLineQueue.setDealTime(new Date());
                dealLineQueue.setDealStatus(ForecastDealStatusSEO.ANALYSIS_ED.getStatus());
                dealLineWriteRepository.update(dealLineQueue);
            }
            forecastOrderLineSDO.setDealStatus(ForecastDealStatusSEO.ANALYSIS_ED.getStatus());
            if (!forecastOrderLineWriteRepository.updateForAnalysis(forecastOrderLineSDO)) {
                log.warn("analysisDeal Save forecast order line:{} failed", forecastOrderLineSDO.getId());
            }
        } catch (Exception e) {
            log.error("ERROR:Set deal status to analyzed faild:", e);
            //TODO FIX 异常建议直接阻断，逻辑处理起来简单些
        }

    }

    /**
     * 设置所有订单处理状态为完全满足，并写入数据库
     */
    public void setRemainOrderLineListDealStatusToTotallySatisfy(AnalyzeProcessDO processDO) {
        if (CollectionUtils.isEmpty(processDO.getOrderLineListToAnalyze())) {
            return;
        }
        List<ForecastOrderLineSDO> orderLineListToFinish = new ArrayList<>(processDO.getOrderLineListToAnalyze());
        finishOrderLineListForReason(orderLineListToFinish, AnalyzeReasonsSEO.TOTALLY_SATISFIED, processDO);
//        finishOrderLineListForRemain(orderLineListToFinish, AnalyzeReasonsSEO.TOTALLY_SATISFIED, processDO);
    }

    /**
     * 完全满足子单需要的库存量，即将预报量设置为预报回复量
     *
     * @param orderLineList 需要满足的子单列表
     */
    private void totallySatisfyOrderLineListInventory(List<ForecastOrderLineSDO> orderLineList, AnalyzeProcessDO processDO) {
        Nullable.stream(orderLineList).forEach(it -> {
            it.setPredictReplyAmount(Integer.parseInt(it.getQuantity()));
        });
        processDO.setLastMessage("完全满足");
        processDO.bizAnalyzeLogWithDistributeInventory(orderLineList);
    }

    /**
     * 完成一个子单的分析
     *
     * @param orderLine
     */
    private void finishOneOrderLine(ForecastOrderLineSDO orderLine, AnalyzeProcessDO processDO) {
        //检查完成，放入结果列表
        saveDealStatusToAnalyzed(orderLine, processDO);
        processDO.finishAnalyze(orderLine);
    }


    /**
     * 完成一个子单的分析，原因是月度回复量检查失败
     *
     * @param orderLine 月度回复量检查失败的子单
     */
    private void finishOneOrderLineForReason(ForecastOrderLineSDO orderLine,
                                             AnalyzeReasonsSEO analyzeReasons, AnalyzeProcessDO processDO) {
        setOneOrderLineDealStatusWithReason(orderLine, analyzeReasons);
        finishOneOrderLine(orderLine, processDO);
    }

    /**
     * 临时修复分配量为0，仍为完全满足问题
     * 完成动作：
     * 1、设置子单处理状态
     * 2、加入分析完成列表，从待分析列表中移除
     * 3、记录日志
     *
     * @param forecastOrderLineList 待完成的子单列表
     * @param analyzeReasons        分析原因
     * @param processDO             封装参数
     */
    private void finishOrderLineListForRemain(List<ForecastOrderLineSDO> forecastOrderLineList,
                                              AnalyzeReasonsSEO analyzeReasons, AnalyzeProcessDO processDO) {
        if (CollectionUtils.isEmpty(forecastOrderLineList)) {
            return;
        }
        for (ForecastOrderLineSDO orderLine : forecastOrderLineList) {
            //TODO:需要排查分配量为0，仍为完全满足的问题
            if (orderLine.getPredictReplyAmount() == 0 ||
                    Integer.parseInt(orderLine.getQuantity()) != orderLine.getPredictReplyAmount() + orderLine.getActualTotalReplyAmount()) {
                finishOneOrderLineForReason(orderLine, AnalyzeReasonsSEO.INVENTORY_SHORTAGE, processDO);
            } else {
                finishOneOrderLineForReason(orderLine, analyzeReasons, processDO);
            }
        }
    }

    /**
     * 完成分析一个子单列表，原因是指定原因
     * 完成动作：
     * 1、设置子单处理状态
     * 2、加入分析完成列表，从待分析列表中移除
     * 3、记录日志
     *
     * @param forecastOrderLineList 待完成的子单列表
     * @param analyzeReasons        分析原因
     * @param processDO             封装参数
     */
    private void finishOrderLineListForReason(List<ForecastOrderLineSDO> forecastOrderLineList,
                                              AnalyzeReasonsSEO analyzeReasons, AnalyzeProcessDO processDO) {
        if (CollectionUtils.isEmpty(forecastOrderLineList)) {
            return;
        }
        for (ForecastOrderLineSDO orderLine : forecastOrderLineList) {
            finishOneOrderLineForReason(orderLine, analyzeReasons, processDO);
        }
    }

    /**
     * 设置分析状态为分析中，子单deal_status及deal_line status都设置为1并保存数据库
     *
     * @param forecastOrderLineEntities 需要设置的子单集合
     * @param batchNo                   批次号
     * @return 成功：true
     * 失败：false
     */
    @EpochTransactional(appCode = "cr_forecast_sales", dataSourceCode = "cr_ots")
    public boolean setBatchAnalyzeStatusToAnalyzing(List<ForecastOrderLineEntity> forecastOrderLineEntities,
                                                    String batchNo) {
        if (CollectionUtils.isEmpty(forecastOrderLineEntities)) {
            return false;
        }
        // 将所有需要分析的订单状态置为1：分析中
        ForecastOrderLineEntity.setOrderLineStatusToAnalyzing(forecastOrderLineEntities);
        createDealLineRecordAndSave(forecastOrderLineEntities, batchNo);

        return true;
    }

    /**
     * 创建dealline记录并保存
     *
     * @param forecastOrderLineEntities
     * @param batchNo
     */
    private void createDealLineRecordAndSave(List<ForecastOrderLineEntity> forecastOrderLineEntities, String batchNo) {
        LoginUser loginUser = epochLoginUserService.getLoginUser();
        if (CollectionUtils.isEmpty(forecastOrderLineEntities)) {
            return;
        }
        try {
            //将数据的信息插入分析队列的表中
            List<DealLineSDO> orderLineListToDeal = Nullable.stream(forecastOrderLineEntities).map(it -> {
                ForecastOrderLineSDO forecastOrderLineSDO = it.getForecastOrderLineSDO();
                DealLineSDO dealLineSDO = new DealLineSDO();
                dealLineSDO.setForecastOrderLineId(forecastOrderLineSDO.getId().toString());
                dealLineSDO.setDealStatus(ForecastDealStatusSEO.ANALYSIS_ING.getStatus());
                dealLineSDO.setDealType(ForecastDealTypeSEO.BATCH_ANALYSIS.getDealTypeCode().toString());
                dealLineSDO.setModifierName(forecastOrderLineSDO.getModifierName());
                dealLineSDO.setModifierId(forecastOrderLineSDO.getModifierId());
                dealLineSDO.setDealTime(new Date());
                //TODO:wm
                dealLineSDO.setStatus(1);
                dealLineSDO.setBatchNumber(batchNo);

                //添加日志
//                LogUtils.printOperateLog(forecastOrderLineSDO.getMainOrderId(), ForecastOrderLogEnum.BATCH_ANALYSIS.getForecastOrderLogValue(), "分析订单", loginUser);
                return dealLineSDO;
            }).collect(Collectors.toList());

            //创建分析队列的表中数据
            dealLineWriteRepository.batchCreate(orderLineListToDeal);
        } catch (Exception e) {
            log.error("ERROR:Create deal line record failed:", e);
//            e.printStackTrace();

            //TODO 这里应该抛异常阻塞的，不要打printStackTrace
        }

    }

    /**
     *  仅加入队列无活动时 去掉活动标记
     * @param forecastOrderLineSDOList
     */
    private void removeActivityMark(List<ForecastOrderLineSDO> forecastOrderLineSDOList){
        if (CollectionUtils.isEmpty(forecastOrderLineSDOList)){
            return;
        }
        for (ForecastOrderLineSDO forecastOrderLineSDO : forecastOrderLineSDOList) {
            //修改活动id
            forecastOrderLineSDO.setActivityCode("");
            //修改活动名称
            forecastOrderLineSDO.setActivityName("");
            //修改活动金额,这个不做设置
            forecastOrderLineSDO.setActivityPrice(0L);

            //修改活动总金额,这个不做设置
            forecastOrderLineSDO.setTotalActivityFee(0L);

            //修改活动赠品情况
            Map<String, Object> features = forecastOrderLineSDO.getFeatures();
            //删除赠品信息
            features.remove(ForecastConstants.GIFTLIST);
            //删除活动标识
            features.remove(ForecastConstants.ACTIVITY_TAG);
            //删除活动详情
            features.remove(ForecastConstants.ACTIVITY_DETAILS);
        }
    }
}
